summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Breitenlohner <peb@mppmu.mpg.de>2009-05-25 13:57:51 +0000
committerPeter Breitenlohner <peb@mppmu.mpg.de>2009-05-25 13:57:51 +0000
commitd8a3e1e0ee206a616a1e7f09f17b1b039a272bc4 (patch)
treee92a17253daea37906a3bfb1611fde2679d0ac02
parentb50e8d78f4b4b86177863a60bc85f5cd393ba04b (diff)
remove mpware/ and old (Pascal Web) mpost and dvitomp
git-svn-id: svn://tug.org/texlive/trunk@13458 c570f23f-e606-0410-a88d-b1316a301751
-rw-r--r--Build/source/texk/web2c/ChangeLog7
-rw-r--r--Build/source/texk/web2c/Makefile.am5
-rw-r--r--Build/source/texk/web2c/Makefile.in465
-rw-r--r--Build/source/texk/web2c/am/web.am13
-rwxr-xr-xBuild/source/texk/web2c/configure3
-rw-r--r--Build/source/texk/web2c/configure.ac3
-rw-r--r--Build/source/texk/web2c/dvitomp.ch837
-rw-r--r--Build/source/texk/web2c/dvitomp.mk27
-rw-r--r--Build/source/texk/web2c/dvitomp.web1680
-rw-r--r--Build/source/texk/web2c/man/Makefile.am9
-rw-r--r--Build/source/texk/web2c/man/Makefile.in16
-rw-r--r--Build/source/texk/web2c/mp.ch2100
-rw-r--r--Build/source/texk/web2c/mp.web24680
-rw-r--r--Build/source/texk/web2c/mplibdir/ChangeLog5
-rw-r--r--Build/source/texk/web2c/mplibdir/am/mplib.am4
-rwxr-xr-xBuild/source/texk/web2c/mplibdir/dvitomp.test (renamed from Build/source/texk/web2c/dvitomp.test)4
16 files changed, 122 insertions, 29736 deletions
diff --git a/Build/source/texk/web2c/ChangeLog b/Build/source/texk/web2c/ChangeLog
index 638fb42bfd0..068e5b09b8c 100644
--- a/Build/source/texk/web2c/ChangeLog
+++ b/Build/source/texk/web2c/ChangeLog
@@ -1,3 +1,10 @@
+2009-05-25 Peter Breitenlohner <peb@mppmu.mpg.de>
+
+ * mp.ch, mp.web, mpdir/, mpware/: removed (old MetaPost).
+ * Makefile.am, configure.ac: removed old MetaPost and mpware.
+ * dvitomp.ch, dvitomp.test, dvitomp.web: removed.
+ * am/web.am: removed dvitomp.
+
2009-05-23 Karl Berry <karl@tug.org>
* tex.ch (pool_size): increase default to 200000, so pdftex -ini
diff --git a/Build/source/texk/web2c/Makefile.am b/Build/source/texk/web2c/Makefile.am
index 1b8169e1378..0cba816c221 100644
--- a/Build/source/texk/web2c/Makefile.am
+++ b/Build/source/texk/web2c/Makefile.am
@@ -9,7 +9,7 @@ INCLUDES = -I$(top_builddir)/.. $(KPATHSEA_INCLUDES)
version = $(WEB2CVERSION)
-SUBDIRS = doc lib man mpware web2c . window
+SUBDIRS = doc lib man web2c . window
if OMFONTS
SUBDIRS += omegafonts
endif OMFONTS
@@ -131,9 +131,6 @@ include $(srcdir)/am/cweb.am
## TeX and MF
include $(srcdir)/am/texmf.am
-## old MetaPost
-include $(srcdir)/mpdir/am/mp.am
-
## MetaPost
include $(srcdir)/mplibdir/am/mplib.am
diff --git a/Build/source/texk/web2c/Makefile.in b/Build/source/texk/web2c/Makefile.in
index 6ee32a2dad9..09a6989af48 100644
--- a/Build/source/texk/web2c/Makefile.in
+++ b/Build/source/texk/web2c/Makefile.in
@@ -36,10 +36,10 @@ build_triplet = @build@
host_triplet = @host@
@OMFONTS_TRUE@am__append_1 = omegafonts
EXTRA_PROGRAMS = $(am__EXEEXT_1) tex$(EXEEXT) mf$(EXEEXT) \
- mf-nowin$(EXEEXT) oldmpost$(EXEEXT) mpost$(EXEEXT) \
- etex$(EXEEXT) pdftex$(EXEEXT) ttf2afm$(EXEEXT) \
- pdftosrc$(EXEEXT) luatex$(EXEEXT) luatangle$(EXEEXT) \
- xetex$(EXEEXT) $(am__EXEEXT_4) aleph$(EXEEXT)
+ mf-nowin$(EXEEXT) mpost$(EXEEXT) etex$(EXEEXT) pdftex$(EXEEXT) \
+ ttf2afm$(EXEEXT) pdftosrc$(EXEEXT) luatex$(EXEEXT) \
+ luatangle$(EXEEXT) xetex$(EXEEXT) $(am__EXEEXT_4) \
+ aleph$(EXEEXT)
bin_PROGRAMS = tangle$(EXEEXT) ctangle$(EXEEXT) $(am__EXEEXT_5) \
ctie$(EXEEXT) cweave$(EXEEXT) tie$(EXEEXT) $(am__EXEEXT_6) \
$(am__EXEEXT_7) $(am__EXEEXT_8) $(am__EXEEXT_9) \
@@ -72,7 +72,7 @@ DIST_COMMON = README $(am__configure_deps) $(dist_man_MANS) \
$(srcdir)/luatexdir/am/luamisc.am \
$(srcdir)/luatexdir/am/luasocket.am \
$(srcdir)/luatexdir/am/luatangle.am \
- $(srcdir)/luatexdir/am/luatex.am $(srcdir)/mpdir/am/mp.am \
+ $(srcdir)/luatexdir/am/luatex.am \
$(srcdir)/mplibdir/am/libmplib.am \
$(srcdir)/mplibdir/am/mplib.am \
$(srcdir)/omegaware/am/omegaware.am \
@@ -97,7 +97,7 @@ DIST_COMMON = README $(am__configure_deps) $(dist_man_MANS) \
@MFN_TRUE@@MF_TRUE@am__append_8 = mf-nowin
@MF_TRUE@am__append_9 = $(mf_tests)
@MF_TRUE@am__append_10 = gftype tftopl
-@MP_TRUE@am__append_11 = oldmpost mpost
+@MP_TRUE@am__append_11 = mpost
@MP_TRUE@am__append_12 = $(mp_tests)
@MP_TRUE@am__append_13 = pltotf tftopl
@ETEX_TRUE@am__append_14 = etex
@@ -346,16 +346,6 @@ nodist_libmplib_a_OBJECTS = libmplib_a-memio.$(OBJEXT) \
$(am__objects_4)
libmplib_a_OBJECTS = $(am_libmplib_a_OBJECTS) \
$(nodist_libmplib_a_OBJECTS)
-liboldmpost_a_AR = $(AR) $(ARFLAGS)
-liboldmpost_a_LIBADD =
-am_liboldmpost_a_OBJECTS = liboldmpost_a-avl.$(OBJEXT) \
- liboldmpost_a-avlstuff.$(OBJEXT) \
- liboldmpost_a-mapfile.$(OBJEXT) \
- liboldmpost_a-turningnumber.$(OBJEXT) \
- liboldmpost_a-utils.$(OBJEXT) liboldmpost_a-writeenc.$(OBJEXT) \
- liboldmpost_a-writefont.$(OBJEXT) \
- liboldmpost_a-writet1.$(OBJEXT)
-liboldmpost_a_OBJECTS = $(am_liboldmpost_a_OBJECTS)
libpdftex_a_AR = $(AR) $(ARFLAGS)
libpdftex_a_LIBADD =
am__libpdftex_a_SOURCES_DIST = pdftexdir/avl.c pdftexdir/avl.h \
@@ -445,11 +435,11 @@ am_libzzip_a_OBJECTS = libzzip_a-dir.$(OBJEXT) libzzip_a-err.$(OBJEXT) \
libzzip_a-stat.$(OBJEXT) libzzip_a-write.$(OBJEXT) \
libzzip_a-zip.$(OBJEXT)
libzzip_a_OBJECTS = $(am_libzzip_a_OBJECTS)
-am__EXEEXT_1 = bibtex$(EXEEXT) dvicopy$(EXEEXT) dvitomp$(EXEEXT) \
- dvitype$(EXEEXT) gftodvi$(EXEEXT) gftopk$(EXEEXT) \
- gftype$(EXEEXT) mft$(EXEEXT) patgen$(EXEEXT) pktogf$(EXEEXT) \
- pktype$(EXEEXT) pltotf$(EXEEXT) pooltype$(EXEEXT) \
- tftopl$(EXEEXT) vftovp$(EXEEXT) vptovf$(EXEEXT) weave$(EXEEXT)
+am__EXEEXT_1 = bibtex$(EXEEXT) dvicopy$(EXEEXT) dvitype$(EXEEXT) \
+ gftodvi$(EXEEXT) gftopk$(EXEEXT) gftype$(EXEEXT) mft$(EXEEXT) \
+ patgen$(EXEEXT) pktogf$(EXEEXT) pktype$(EXEEXT) \
+ pltotf$(EXEEXT) pooltype$(EXEEXT) tftopl$(EXEEXT) \
+ vftovp$(EXEEXT) vptovf$(EXEEXT) weave$(EXEEXT)
am__EXEEXT_2 = odvicopy$(EXEEXT) odvitype$(EXEEXT) otangle$(EXEEXT)
am__EXEEXT_3 = ofm2opl$(EXEEXT) opl2ofm$(EXEEXT) ovf2ovp$(EXEEXT) \
ovp2ovf$(EXEEXT)
@@ -458,7 +448,7 @@ am__EXEEXT_4 = $(am__EXEEXT_2) $(am__EXEEXT_3)
@TEX_TRUE@am__EXEEXT_6 = tex$(EXEEXT)
@MF_TRUE@am__EXEEXT_7 = mf$(EXEEXT)
@MFN_TRUE@@MF_TRUE@am__EXEEXT_8 = mf-nowin$(EXEEXT)
-@MP_TRUE@am__EXEEXT_9 = oldmpost$(EXEEXT) mpost$(EXEEXT)
+@MP_TRUE@am__EXEEXT_9 = mpost$(EXEEXT)
@ETEX_TRUE@am__EXEEXT_10 = etex$(EXEEXT)
@PDFTEX_TRUE@am__EXEEXT_11 = pdftex$(EXEEXT) ttf2afm$(EXEEXT) \
@PDFTEX_TRUE@ pdftosrc$(EXEEXT)
@@ -510,10 +500,6 @@ nodist_dvicopy_OBJECTS = dvicopy.$(OBJEXT)
dvicopy_OBJECTS = $(nodist_dvicopy_OBJECTS)
dvicopy_LDADD = $(LDADD)
dvicopy_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
-nodist_dvitomp_OBJECTS = dvitomp.$(OBJEXT)
-dvitomp_OBJECTS = $(nodist_dvitomp_OBJECTS)
-dvitomp_LDADD = $(LDADD)
-dvitomp_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
nodist_dvitype_OBJECTS = dvitype.$(OBJEXT)
dvitype_OBJECTS = $(nodist_dvitype_OBJECTS)
dvitype_LDADD = $(LDADD)
@@ -588,12 +574,6 @@ nodist_ofm2opl_OBJECTS = ofm2opl.$(OBJEXT)
ofm2opl_OBJECTS = $(nodist_ofm2opl_OBJECTS)
ofm2opl_LDADD = $(LDADD)
ofm2opl_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
-am__objects_14 = oldmpost-mpini.$(OBJEXT) oldmpost-mp0.$(OBJEXT) \
- oldmpost-mp1.$(OBJEXT) oldmpost-mp2.$(OBJEXT)
-nodist_oldmpost_OBJECTS = $(am__objects_14) oldmpost-mp-pool.$(OBJEXT) \
- oldmpost-mpextra.$(OBJEXT)
-oldmpost_OBJECTS = $(nodist_oldmpost_OBJECTS)
-oldmpost_DEPENDENCIES = liboldmpost.a $(am__DEPENDENCIES_2)
nodist_opl2ofm_OBJECTS = opl2ofm.$(OBJEXT)
opl2ofm_OBJECTS = $(nodist_opl2ofm_OBJECTS)
opl2ofm_LDADD = $(LDADD)
@@ -617,12 +597,12 @@ patgen_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
am__dist_pdftex_SOURCES_DIST = synctexdir/synctex.h \
synctexdir/synctex-common.h synctexdir/synctex-pdftex.h
dist_pdftex_OBJECTS =
-am__objects_15 = pdftex-pdftexini.$(OBJEXT) pdftex-pdftex0.$(OBJEXT) \
+am__objects_14 = pdftex-pdftexini.$(OBJEXT) pdftex-pdftex0.$(OBJEXT) \
pdftex-pdftex1.$(OBJEXT) pdftex-pdftex2.$(OBJEXT) \
pdftex-pdftex3.$(OBJEXT)
-@PDFTEX_SYNCTEX_TRUE@am__objects_16 = pdftex-synctex-pdf.$(OBJEXT)
-nodist_pdftex_OBJECTS = $(am__objects_15) pdftex-pdftex-pool.$(OBJEXT) \
- pdftex-pdftexextra.$(OBJEXT) $(am__objects_16)
+@PDFTEX_SYNCTEX_TRUE@am__objects_15 = pdftex-synctex-pdf.$(OBJEXT)
+nodist_pdftex_OBJECTS = $(am__objects_14) pdftex-pdftex-pool.$(OBJEXT) \
+ pdftex-pdftexextra.$(OBJEXT) $(am__objects_15)
pdftex_OBJECTS = $(dist_pdftex_OBJECTS) $(nodist_pdftex_OBJECTS)
am__DEPENDENCIES_6 = libpdftex.a $(am__DEPENDENCIES_1) \
$(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \
@@ -659,11 +639,11 @@ tangleboot_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
am__dist_tex_SOURCES_DIST = synctexdir/synctex.h \
synctexdir/synctex-common.h synctexdir/synctex-tex.h
dist_tex_OBJECTS =
-am__objects_17 = tex-texini.$(OBJEXT) tex-tex0.$(OBJEXT) \
+am__objects_16 = tex-texini.$(OBJEXT) tex-tex0.$(OBJEXT) \
tex-tex1.$(OBJEXT) tex-tex2.$(OBJEXT)
-@TEX_SYNCTEX_TRUE@am__objects_18 = tex-synctex.$(OBJEXT)
-nodist_tex_OBJECTS = $(am__objects_17) tex-tex-pool.$(OBJEXT) \
- tex-texextra.$(OBJEXT) $(am__objects_18)
+@TEX_SYNCTEX_TRUE@am__objects_17 = tex-synctex.$(OBJEXT)
+nodist_tex_OBJECTS = $(am__objects_16) tex-tex-pool.$(OBJEXT) \
+ tex-texextra.$(OBJEXT) $(am__objects_17)
tex_OBJECTS = $(dist_tex_OBJECTS) $(nodist_tex_OBJECTS)
@TEX_SYNCTEX_TRUE@am__DEPENDENCIES_7 = $(am__DEPENDENCIES_1)
tex_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \
@@ -695,11 +675,11 @@ weave_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1)
am__dist_xetex_SOURCES_DIST = synctexdir/synctex.h \
synctexdir/synctex-common.h synctexdir/synctex-xetex.h
dist_xetex_OBJECTS =
-am__objects_19 = xetex-xetexini.$(OBJEXT) xetex-xetex0.$(OBJEXT) \
+am__objects_18 = xetex-xetexini.$(OBJEXT) xetex-xetex0.$(OBJEXT) \
xetex-xetex1.$(OBJEXT) xetex-xetex2.$(OBJEXT)
-@XETEX_SYNCTEX_TRUE@am__objects_20 = xetex-synctex-xe.$(OBJEXT)
-nodist_xetex_OBJECTS = $(am__objects_19) xetex-xetex_pool.$(OBJEXT) \
- xetex-xetexextra.$(OBJEXT) $(am__objects_20)
+@XETEX_SYNCTEX_TRUE@am__objects_19 = xetex-synctex-xe.$(OBJEXT)
+nodist_xetex_OBJECTS = $(am__objects_18) xetex-xetex_pool.$(OBJEXT) \
+ xetex-xetexextra.$(OBJEXT) $(am__objects_19)
xetex_OBJECTS = $(dist_xetex_OBJECTS) $(nodist_xetex_OBJECTS)
@XETEX_MACOSX_FALSE@am__DEPENDENCIES_8 = $(am__DEPENDENCIES_1) \
@XETEX_MACOSX_FALSE@ $(am__DEPENDENCIES_1) \
@@ -738,14 +718,13 @@ SOURCES = $(libff_a_SOURCES) $(liblua51_a_SOURCES) \
$(libluasocket_a_SOURCES) $(libluatex_a_SOURCES) \
$(EXTRA_libluatex_a_SOURCES) $(libmd5_a_SOURCES) \
$(nodist_libmf_a_SOURCES) $(libmplib_a_SOURCES) \
- $(nodist_libmplib_a_SOURCES) $(liboldmpost_a_SOURCES) \
- $(libpdftex_a_SOURCES) $(EXTRA_libpdftex_a_SOURCES) \
- $(libxetex_a_SOURCES) $(libxetexmm_a_SOURCES) \
- $(libzzip_a_SOURCES) $(aleph_SOURCES) $(nodist_aleph_SOURCES) \
- $(nodist_bibtex_SOURCES) $(nodist_ctangle_SOURCES) \
- $(nodist_ctangleboot_SOURCES) $(nodist_ctie_SOURCES) \
- $(nodist_cweave_SOURCES) $(nodist_dvicopy_SOURCES) \
- $(nodist_dvitomp_SOURCES) $(nodist_dvitype_SOURCES) \
+ $(nodist_libmplib_a_SOURCES) $(libpdftex_a_SOURCES) \
+ $(EXTRA_libpdftex_a_SOURCES) $(libxetex_a_SOURCES) \
+ $(libxetexmm_a_SOURCES) $(libzzip_a_SOURCES) $(aleph_SOURCES) \
+ $(nodist_aleph_SOURCES) $(nodist_bibtex_SOURCES) \
+ $(nodist_ctangle_SOURCES) $(nodist_ctangleboot_SOURCES) \
+ $(nodist_ctie_SOURCES) $(nodist_cweave_SOURCES) \
+ $(nodist_dvicopy_SOURCES) $(nodist_dvitype_SOURCES) \
$(dist_etex_SOURCES) $(nodist_etex_SOURCES) $(gftodvi_SOURCES) \
$(nodist_gftodvi_SOURCES) $(nodist_gftopk_SOURCES) \
$(nodist_gftype_SOURCES) $(nodist_luatangle_SOURCES) \
@@ -754,29 +733,28 @@ SOURCES = $(libff_a_SOURCES) $(liblua51_a_SOURCES) \
$(nodist_mf_nowin_SOURCES) $(nodist_mft_SOURCES) \
$(nodist_mpost_SOURCES) $(nodist_odvicopy_SOURCES) \
$(nodist_odvitype_SOURCES) $(nodist_ofm2opl_SOURCES) \
- $(nodist_oldmpost_SOURCES) $(nodist_opl2ofm_SOURCES) \
- $(nodist_otangle_SOURCES) $(nodist_ovf2ovp_SOURCES) \
- $(nodist_ovp2ovf_SOURCES) $(nodist_patgen_SOURCES) \
- $(dist_pdftex_SOURCES) $(nodist_pdftex_SOURCES) \
- $(nodist_EXTRA_pdftex_SOURCES) $(pdftosrc_SOURCES) \
- $(nodist_EXTRA_pdftosrc_SOURCES) $(nodist_pktogf_SOURCES) \
- $(nodist_pktype_SOURCES) $(nodist_pltotf_SOURCES) \
- $(nodist_pooltype_SOURCES) $(synctex_SOURCES) \
- $(nodist_tangle_SOURCES) $(nodist_tangleboot_SOURCES) \
- $(dist_tex_SOURCES) $(nodist_tex_SOURCES) \
- $(nodist_tftopl_SOURCES) $(nodist_tie_SOURCES) \
- $(ttf2afm_SOURCES) $(EXTRA_ttf2afm_SOURCES) \
- $(nodist_vftovp_SOURCES) $(nodist_vptovf_SOURCES) \
- $(nodist_weave_SOURCES) $(dist_xetex_SOURCES) \
- $(nodist_xetex_SOURCES) $(nodist_EXTRA_xetex_SOURCES)
+ $(nodist_opl2ofm_SOURCES) $(nodist_otangle_SOURCES) \
+ $(nodist_ovf2ovp_SOURCES) $(nodist_ovp2ovf_SOURCES) \
+ $(nodist_patgen_SOURCES) $(dist_pdftex_SOURCES) \
+ $(nodist_pdftex_SOURCES) $(nodist_EXTRA_pdftex_SOURCES) \
+ $(pdftosrc_SOURCES) $(nodist_EXTRA_pdftosrc_SOURCES) \
+ $(nodist_pktogf_SOURCES) $(nodist_pktype_SOURCES) \
+ $(nodist_pltotf_SOURCES) $(nodist_pooltype_SOURCES) \
+ $(synctex_SOURCES) $(nodist_tangle_SOURCES) \
+ $(nodist_tangleboot_SOURCES) $(dist_tex_SOURCES) \
+ $(nodist_tex_SOURCES) $(nodist_tftopl_SOURCES) \
+ $(nodist_tie_SOURCES) $(ttf2afm_SOURCES) \
+ $(EXTRA_ttf2afm_SOURCES) $(nodist_vftovp_SOURCES) \
+ $(nodist_vptovf_SOURCES) $(nodist_weave_SOURCES) \
+ $(dist_xetex_SOURCES) $(nodist_xetex_SOURCES) \
+ $(nodist_EXTRA_xetex_SOURCES)
DIST_SOURCES = $(libff_a_SOURCES) $(liblua51_a_SOURCES) \
$(libluamisc_a_SOURCES) $(EXTRA_libluamisc_a_SOURCES) \
$(libluasocket_a_SOURCES) $(libluatex_a_SOURCES) \
$(EXTRA_libluatex_a_SOURCES) $(libmd5_a_SOURCES) \
- $(libmplib_a_SOURCES) $(liboldmpost_a_SOURCES) \
- $(am__libpdftex_a_SOURCES_DIST) $(EXTRA_libpdftex_a_SOURCES) \
- $(am__libxetex_a_SOURCES_DIST) $(libxetexmm_a_SOURCES) \
- $(libzzip_a_SOURCES) $(aleph_SOURCES) \
+ $(libmplib_a_SOURCES) $(am__libpdftex_a_SOURCES_DIST) \
+ $(EXTRA_libpdftex_a_SOURCES) $(am__libxetex_a_SOURCES_DIST) \
+ $(libxetexmm_a_SOURCES) $(libzzip_a_SOURCES) $(aleph_SOURCES) \
$(am__dist_etex_SOURCES_DIST) $(gftodvi_SOURCES) \
$(md5main_SOURCES) $(am__dist_pdftex_SOURCES_DIST) \
$(pdftosrc_SOURCES) $(synctex_SOURCES) \
@@ -823,13 +801,13 @@ ETAGS = etags
CTAGS = ctags
am__tty_colors = \
red=; grn=; lgn=; blu=; std=
-am__EXEEXT_21 = bibtex.test dvicopy.test dvitomp.test dvitype.test \
- gftodvi.test gftopk.test gftype.test mft.test patgen.test \
- pktogf.test pktype.test pltotf.test pooltype.test tftopl.test \
- vftovp.test vptovf.test weave.test
+am__EXEEXT_21 = bibtex.test dvicopy.test dvitype.test gftodvi.test \
+ gftopk.test gftype.test mft.test patgen.test pktogf.test \
+ pktype.test pltotf.test pooltype.test tftopl.test vftovp.test \
+ vptovf.test weave.test
am__EXEEXT_22 = $(am__EXEEXT_21)
@WEB_TRUE@am__EXEEXT_23 = $(am__EXEEXT_22)
-DIST_SUBDIRS = doc lib man mpware web2c . window omegafonts otps
+DIST_SUBDIRS = doc lib man web2c . window omegafonts otps
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
distdir = $(PACKAGE)-$(VERSION)
top_distdir = $(distdir)
@@ -1045,13 +1023,13 @@ x_tool_libs = @x_tool_libs@
ACLOCAL_AMFLAGS = -I ../../m4
INCLUDES = -I$(top_builddir)/.. $(KPATHSEA_INCLUDES)
version = $(WEB2CVERSION)
-SUBDIRS = doc lib man mpware web2c . window $(am__append_1) otps
+SUBDIRS = doc lib man web2c . window $(am__append_1) otps
proglib = lib/lib.a
dist_man_MANS = synctexdir/man1/synctex.1 synctexdir/man5/synctex.5
nodist_man_MANS = ctiedir/ctie.1 tiedir/tie.1
-EXTRA_LIBRARIES = libmf.a liboldmpost.a libmplib.a libpdftex.a \
- liblua51.a libzzip.a libluasocket.a libluamisc.a libff.a \
- libluatex.a libxetex.a libxetexmm.a libmd5.a
+EXTRA_LIBRARIES = libmf.a libmplib.a libpdftex.a liblua51.a libzzip.a \
+ libluasocket.a libluamisc.a libff.a libluatex.a libxetex.a \
+ libxetexmm.a libmd5.a
EXTRA_DIST = PROJECTS cftests config.h cpascal.h help.h tangle.web \
tangle.ch tangleboot.p cwebdir ctangleboot.c cwebboot.c \
tangle.test $(web_programs:=.web) $(web_programs:=.ch) \
@@ -1092,22 +1070,20 @@ EXTRA_DIST = PROJECTS cftests config.h cpascal.h help.h tangle.web \
triptrap/trip.fot triptrap/trip.log triptrap/trip.pl \
triptrap/trip.tex triptrap/trip.typ triptrap/trip1.in \
triptrap/trip2.in triptrap/tripin.log triptrap/tripman.tex \
- triptrap/tripos.tex triptrap/writeo triptrap/writeo.2 mp.web \
- mp.ch lib/texmfmp.c mpdir/mp.defines mpdir/mplib.h metapost.mk \
- mpdir/Makefile.in.orig mpdir/mpostlib.mk mplibdir/ChangeLog \
- $(mpost_web) $(mp_tests) $(libmplib_web) mplibdir/lmplib.c \
- $(etex_web_srcs) $(etex_ch_srcs) etexdir/etexextra.in \
- lib/texmfmp.c tmf-pool.h etexdir/ChangeLog etexdir/INSTALL \
- etexdir/README etexdir/etex_gen.tex $(etex_tests) \
- etexdir/etrip/etrip.diffs etexdir/etrip/etrip.fot \
- etexdir/etrip/etrip.log etexdir/etrip/etrip.out \
- etexdir/etrip/etrip.pl etexdir/etrip/etrip.tex \
- etexdir/etrip/etrip.typ etexdir/etrip/etrip1.in \
- etexdir/etrip/etrip2.in etexdir/etrip/etrip3.in \
- etexdir/etrip/etripin.log etexdir/etrip/etripman.tex \
- etexdir/etrip/texmf.cnf etexdir/etrip/trip2.in \
- etexdir/etex.mak etexdir/etex.mk $(pdftex_ch_srcs) \
- pdftexdir/pdftex.defines pdftexdir/pdftex.h \
+ triptrap/tripos.tex triptrap/writeo triptrap/writeo.2 \
+ mplibdir/ChangeLog $(mpost_web) $(mp_tests) $(libmplib_web) \
+ mplibdir/lmplib.c $(etex_web_srcs) $(etex_ch_srcs) \
+ etexdir/etexextra.in lib/texmfmp.c tmf-pool.h \
+ etexdir/ChangeLog etexdir/INSTALL etexdir/README \
+ etexdir/etex_gen.tex $(etex_tests) etexdir/etrip/etrip.diffs \
+ etexdir/etrip/etrip.fot etexdir/etrip/etrip.log \
+ etexdir/etrip/etrip.out etexdir/etrip/etrip.pl \
+ etexdir/etrip/etrip.tex etexdir/etrip/etrip.typ \
+ etexdir/etrip/etrip1.in etexdir/etrip/etrip2.in \
+ etexdir/etrip/etrip3.in etexdir/etrip/etripin.log \
+ etexdir/etrip/etripman.tex etexdir/etrip/texmf.cnf \
+ etexdir/etrip/trip2.in etexdir/etex.mak etexdir/etex.mk \
+ $(pdftex_ch_srcs) pdftexdir/pdftex.defines pdftexdir/pdftex.h \
pdftexdir/pdftexextra.in pdftexdir/ChangeLog pdftexdir/NEWS \
pdftexdir/README pdftexdir/change-files.txt \
pdftexdir/pdftex.mak pdftexdir/Makefile.in.orig \
@@ -1266,20 +1242,19 @@ DISTCLEANFILES = $(EXTRA_LIBRARIES) tangle.c tangle.h tangle.p \
cweb.c stamp-ctangle cftest.p $(web_programs:=.c) \
$(web_programs:=.h) $(web_programs:=.p) \
$(web_programs:=-web2c) tests/xexampl.aux tests/xexampl.bbl \
- tests/xexampl.blg tests/xstory.dvi tests/xpplr.dvi ptmr.mpx \
- tests/xptmr.mpx tests/xstory.mpx tests/xstory.dvityp \
- tests/xpagenum.typ cmr10.dvi tests/xcmr10.dvi cmr10.pk \
- tests/xcmr10.pk tests/xcmr10.gft1 tests/xcmr10.gft2 io.tex \
- tests/io.tex tests/xpatout pattmp.4 cmr10.gf \
- tests/xcmr10.600gf tests/xcmr10.pktyp tests/xcmr10.tfm \
- tests/xtexpool.typ tests/xcmr10.pl tests/xptmr.vpl \
- tests/xptmr.tfm tests/xptmr.vf pooltype.tex ctie.c cweave.c \
- tie.c ctie.outc ctie.outm common.tex common.scn common.idx \
- tie.outc tie.outm $(nodist_tex_SOURCES) tex-final.ch tex-web2c \
- tex.p tex.pool tex-tangle $(nodist_libmf_a_SOURCES) mfextra.c \
- mf-final.ch mf-web2c mf.p mf.pool mf-tangle \
- $(nodist_oldmpost_SOURCES) mp-web2c mp.p mp.pool mp-tangle \
- $(nodist_mpost_SOURCES) mpxout-ctangle \
+ tests/xexampl.blg tests/xstory.dvi tests/xpplr.dvi \
+ tests/xstory.dvityp tests/xpagenum.typ cmr10.dvi \
+ tests/xcmr10.dvi cmr10.pk tests/xcmr10.pk tests/xcmr10.gft1 \
+ tests/xcmr10.gft2 io.tex tests/io.tex tests/xpatout pattmp.4 \
+ cmr10.gf tests/xcmr10.600gf tests/xcmr10.pktyp \
+ tests/xcmr10.tfm tests/xtexpool.typ tests/xcmr10.pl \
+ tests/xptmr.vpl tests/xptmr.tfm tests/xptmr.vf pooltype.tex \
+ ctie.c cweave.c tie.c ctie.outc ctie.outm common.tex \
+ common.scn common.idx tie.outc tie.outm $(nodist_tex_SOURCES) \
+ tex-final.ch tex-web2c tex.p tex.pool tex-tangle \
+ $(nodist_libmf_a_SOURCES) mfextra.c mf-final.ch mf-web2c mf.p \
+ mf.pool mf-tangle $(nodist_mpost_SOURCES) mpxout-ctangle \
+ ptmr.mpx tests/xptmr.mpx tests/xstory.mpx \
$(nodist_libmplib_a_SOURCES) mp-ctangle psout-ctangle \
svgout-ctangle $(nodist_etex_SOURCES) etex.web etex.ch \
etex-web2c etex.p etex.pool etex-tangle etexdir/etex.version \
@@ -1322,12 +1297,11 @@ nodist_tangle_SOURCES = tangle.c tangle.h
nodist_tangleboot_SOURCES = tangleboot.c tangleboot.h
nodist_ctangle_SOURCES = ctangle.c cweb.c
nodist_ctangleboot_SOURCES = ctangleboot.c cwebboot.c
-web_programs = bibtex dvicopy dvitomp dvitype gftodvi gftopk gftype mft \
+web_programs = bibtex dvicopy dvitype gftodvi gftopk gftype mft \
patgen pktogf pktype pltotf pooltype tftopl vftovp vptovf weave
nodist_bibtex_SOURCES = bibtex.c bibtex.h
nodist_dvicopy_SOURCES = dvicopy.c dvicopy.h
-nodist_dvitomp_SOURCES = dvitomp.c dvitomp.h
nodist_dvitype_SOURCES = dvitype.c dvitype.h
nodist_gftodvi_SOURCES = gftodvi.c gftodvi.h
gftodvi_SOURCES = gftodmem.h
@@ -1376,24 +1350,6 @@ mf_c_h = mfini.c mf0.c mf1.c mfcoerce.h mfd.h
nodist_libmf_a_SOURCES = $(mf_c_h) mf-pool.c
libmf_a_CPPFLAGS = -DMETA_FONT
mf_tests = trap.test
-oldmpost_CPPFLAGS = -I$(srcdir)/mpdir
-oldmpost_LDADD = liboldmpost.a $(LDADD)
-oldmpost_c_h = mpini.c mp0.c mp1.c mp2.c mpcoerce.h mpd.h
-nodist_oldmpost_SOURCES = $(oldmpost_c_h) mp-pool.c mpextra.c
-liboldmpost_a_CPPFLAGS = -I$(srcdir)/mpdir
-liboldmpost_a_SOURCES = \
- mpdir/avl.c \
- mpdir/avl.h \
- mpdir/avlstuff.c \
- mpdir/avlstuff.h \
- mpdir/mapfile.c \
- mpdir/mpmac.h \
- mpdir/turningnumber.c \
- mpdir/utils.c \
- mpdir/writeenc.c \
- mpdir/writefont.c \
- mpdir/writet1.c
-
mp_ctangle = CWEBINPUTS=$(srcdir)/mplibdir $(ctangle)
mpost_CPPFLAGS = -I$(srcdir)/mplibdir
mpost_DEPENDENCIES = libmplib.a
@@ -1401,7 +1357,7 @@ mpost_LDADD = libmplib.a $(KPATHSEA_LIBS) -lm
nodist_mpost_SOURCES = mpost.c $(mpxout_c_h)
mpxout_c_h = mpxout.c mpxout.h
mpost_web = mplibdir/mpost.w mplibdir/mpxout.w
-mp_tests = mplibdir/mptrap.test
+mp_tests = mplibdir/dvitomp.test mplibdir/mptrap.test
libmplib_a_CPPFLAGS = -I$(srcdir)/mplibdir
libmplib_a_SOURCES = mplibdir/avl.h mplibdir/avl.c
nodist_libmplib_a_SOURCES = memio.c tfmin.c $(mp_c_h) $(psout_c_h) \
@@ -2086,7 +2042,7 @@ all: c-auto.h ff-config.h
.SUFFIXES: .c .cc .cpp .cxx .lo .o .obj
am--refresh:
@:
-$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mpdir/am/mp.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luatangle.am $(srcdir)/luatexdir/am/liblua51.am $(srcdir)/luatexdir/am/libzzip.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/xetexdir/am/xetex.am $(srcdir)/omegaware/am/omegaware.am $(srcdir)/alephdir/am/aleph.am $(srcdir)/synctexdir/am/synctex.am $(srcdir)/libmd5/am/md5.am $(am__configure_deps)
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luatangle.am $(srcdir)/luatexdir/am/liblua51.am $(srcdir)/luatexdir/am/libzzip.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/xetexdir/am/xetex.am $(srcdir)/omegaware/am/omegaware.am $(srcdir)/alephdir/am/aleph.am $(srcdir)/synctexdir/am/synctex.am $(srcdir)/libmd5/am/md5.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
@@ -2178,10 +2134,6 @@ libmplib.a: $(libmplib_a_OBJECTS) $(libmplib_a_DEPENDENCIES)
-rm -f libmplib.a
$(libmplib_a_AR) libmplib.a $(libmplib_a_OBJECTS) $(libmplib_a_LIBADD)
$(RANLIB) libmplib.a
-liboldmpost.a: $(liboldmpost_a_OBJECTS) $(liboldmpost_a_DEPENDENCIES)
- -rm -f liboldmpost.a
- $(liboldmpost_a_AR) liboldmpost.a $(liboldmpost_a_OBJECTS) $(liboldmpost_a_LIBADD)
- $(RANLIB) liboldmpost.a
libpdftex.a: $(libpdftex_a_OBJECTS) $(libpdftex_a_DEPENDENCIES)
-rm -f libpdftex.a
$(libpdftex_a_AR) libpdftex.a $(libpdftex_a_OBJECTS) $(libpdftex_a_LIBADD)
@@ -2280,9 +2232,6 @@ cweave$(EXEEXT): $(cweave_OBJECTS) $(cweave_DEPENDENCIES)
dvicopy$(EXEEXT): $(dvicopy_OBJECTS) $(dvicopy_DEPENDENCIES)
@rm -f dvicopy$(EXEEXT)
$(LINK) $(dvicopy_OBJECTS) $(dvicopy_LDADD) $(LIBS)
-dvitomp$(EXEEXT): $(dvitomp_OBJECTS) $(dvitomp_DEPENDENCIES)
- @rm -f dvitomp$(EXEEXT)
- $(LINK) $(dvitomp_OBJECTS) $(dvitomp_LDADD) $(LIBS)
dvitype$(EXEEXT): $(dvitype_OBJECTS) $(dvitype_DEPENDENCIES)
@rm -f dvitype$(EXEEXT)
$(LINK) $(dvitype_OBJECTS) $(dvitype_LDADD) $(LIBS)
@@ -2328,9 +2277,6 @@ odvitype$(EXEEXT): $(odvitype_OBJECTS) $(odvitype_DEPENDENCIES)
ofm2opl$(EXEEXT): $(ofm2opl_OBJECTS) $(ofm2opl_DEPENDENCIES)
@rm -f ofm2opl$(EXEEXT)
$(LINK) $(ofm2opl_OBJECTS) $(ofm2opl_LDADD) $(LIBS)
-oldmpost$(EXEEXT): $(oldmpost_OBJECTS) $(oldmpost_DEPENDENCIES)
- @rm -f oldmpost$(EXEEXT)
- $(LINK) $(oldmpost_OBJECTS) $(oldmpost_LDADD) $(LIBS)
opl2ofm$(EXEEXT): $(opl2ofm_OBJECTS) $(opl2ofm_DEPENDENCIES)
@rm -f opl2ofm$(EXEEXT)
$(LINK) $(opl2ofm_OBJECTS) $(opl2ofm_LDADD) $(LIBS)
@@ -2421,7 +2367,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cweb.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cwebboot.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dvicopy.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dvitomp.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dvitype.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etex-etex-pool.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/etex-etex0.Po@am__quote@
@@ -2632,14 +2577,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-psout.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-svgout.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmplib_a-tfmin.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-avl.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-avlstuff.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-mapfile.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-turningnumber.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-utils.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-writeenc.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-writefont.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/liboldmpost_a-writet1.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpdftex_a-avl.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpdftex_a-avlstuff.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libpdftex_a-epdf.Po@am__quote@
@@ -2713,12 +2650,6 @@ distclean-compile:
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/odvicopy.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/odvitype.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ofm2opl.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldmpost-mp-pool.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldmpost-mp0.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldmpost-mp1.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldmpost-mp2.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldmpost-mpextra.Po@am__quote@
-@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/oldmpost-mpini.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/opl2ofm.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/otangle.Po@am__quote@
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ovf2ovp.Po@am__quote@
@@ -5559,118 +5490,6 @@ libmplib_a-svgout.obj: svgout.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libmplib_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o libmplib_a-svgout.obj `if test -f 'svgout.c'; then $(CYGPATH_W) 'svgout.c'; else $(CYGPATH_W) '$(srcdir)/svgout.c'; fi`
-liboldmpost_a-avl.o: mpdir/avl.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-avl.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-avl.Tpo -c -o liboldmpost_a-avl.o `test -f 'mpdir/avl.c' || echo '$(srcdir)/'`mpdir/avl.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-avl.Tpo $(DEPDIR)/liboldmpost_a-avl.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/avl.c' object='liboldmpost_a-avl.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-avl.o `test -f 'mpdir/avl.c' || echo '$(srcdir)/'`mpdir/avl.c
-
-liboldmpost_a-avl.obj: mpdir/avl.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-avl.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-avl.Tpo -c -o liboldmpost_a-avl.obj `if test -f 'mpdir/avl.c'; then $(CYGPATH_W) 'mpdir/avl.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/avl.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-avl.Tpo $(DEPDIR)/liboldmpost_a-avl.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/avl.c' object='liboldmpost_a-avl.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-avl.obj `if test -f 'mpdir/avl.c'; then $(CYGPATH_W) 'mpdir/avl.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/avl.c'; fi`
-
-liboldmpost_a-avlstuff.o: mpdir/avlstuff.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-avlstuff.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-avlstuff.Tpo -c -o liboldmpost_a-avlstuff.o `test -f 'mpdir/avlstuff.c' || echo '$(srcdir)/'`mpdir/avlstuff.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-avlstuff.Tpo $(DEPDIR)/liboldmpost_a-avlstuff.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/avlstuff.c' object='liboldmpost_a-avlstuff.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-avlstuff.o `test -f 'mpdir/avlstuff.c' || echo '$(srcdir)/'`mpdir/avlstuff.c
-
-liboldmpost_a-avlstuff.obj: mpdir/avlstuff.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-avlstuff.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-avlstuff.Tpo -c -o liboldmpost_a-avlstuff.obj `if test -f 'mpdir/avlstuff.c'; then $(CYGPATH_W) 'mpdir/avlstuff.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/avlstuff.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-avlstuff.Tpo $(DEPDIR)/liboldmpost_a-avlstuff.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/avlstuff.c' object='liboldmpost_a-avlstuff.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-avlstuff.obj `if test -f 'mpdir/avlstuff.c'; then $(CYGPATH_W) 'mpdir/avlstuff.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/avlstuff.c'; fi`
-
-liboldmpost_a-mapfile.o: mpdir/mapfile.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-mapfile.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-mapfile.Tpo -c -o liboldmpost_a-mapfile.o `test -f 'mpdir/mapfile.c' || echo '$(srcdir)/'`mpdir/mapfile.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-mapfile.Tpo $(DEPDIR)/liboldmpost_a-mapfile.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/mapfile.c' object='liboldmpost_a-mapfile.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-mapfile.o `test -f 'mpdir/mapfile.c' || echo '$(srcdir)/'`mpdir/mapfile.c
-
-liboldmpost_a-mapfile.obj: mpdir/mapfile.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-mapfile.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-mapfile.Tpo -c -o liboldmpost_a-mapfile.obj `if test -f 'mpdir/mapfile.c'; then $(CYGPATH_W) 'mpdir/mapfile.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/mapfile.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-mapfile.Tpo $(DEPDIR)/liboldmpost_a-mapfile.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/mapfile.c' object='liboldmpost_a-mapfile.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-mapfile.obj `if test -f 'mpdir/mapfile.c'; then $(CYGPATH_W) 'mpdir/mapfile.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/mapfile.c'; fi`
-
-liboldmpost_a-turningnumber.o: mpdir/turningnumber.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-turningnumber.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-turningnumber.Tpo -c -o liboldmpost_a-turningnumber.o `test -f 'mpdir/turningnumber.c' || echo '$(srcdir)/'`mpdir/turningnumber.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-turningnumber.Tpo $(DEPDIR)/liboldmpost_a-turningnumber.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/turningnumber.c' object='liboldmpost_a-turningnumber.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-turningnumber.o `test -f 'mpdir/turningnumber.c' || echo '$(srcdir)/'`mpdir/turningnumber.c
-
-liboldmpost_a-turningnumber.obj: mpdir/turningnumber.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-turningnumber.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-turningnumber.Tpo -c -o liboldmpost_a-turningnumber.obj `if test -f 'mpdir/turningnumber.c'; then $(CYGPATH_W) 'mpdir/turningnumber.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/turningnumber.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-turningnumber.Tpo $(DEPDIR)/liboldmpost_a-turningnumber.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/turningnumber.c' object='liboldmpost_a-turningnumber.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-turningnumber.obj `if test -f 'mpdir/turningnumber.c'; then $(CYGPATH_W) 'mpdir/turningnumber.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/turningnumber.c'; fi`
-
-liboldmpost_a-utils.o: mpdir/utils.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-utils.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-utils.Tpo -c -o liboldmpost_a-utils.o `test -f 'mpdir/utils.c' || echo '$(srcdir)/'`mpdir/utils.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-utils.Tpo $(DEPDIR)/liboldmpost_a-utils.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/utils.c' object='liboldmpost_a-utils.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-utils.o `test -f 'mpdir/utils.c' || echo '$(srcdir)/'`mpdir/utils.c
-
-liboldmpost_a-utils.obj: mpdir/utils.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-utils.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-utils.Tpo -c -o liboldmpost_a-utils.obj `if test -f 'mpdir/utils.c'; then $(CYGPATH_W) 'mpdir/utils.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/utils.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-utils.Tpo $(DEPDIR)/liboldmpost_a-utils.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/utils.c' object='liboldmpost_a-utils.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-utils.obj `if test -f 'mpdir/utils.c'; then $(CYGPATH_W) 'mpdir/utils.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/utils.c'; fi`
-
-liboldmpost_a-writeenc.o: mpdir/writeenc.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-writeenc.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-writeenc.Tpo -c -o liboldmpost_a-writeenc.o `test -f 'mpdir/writeenc.c' || echo '$(srcdir)/'`mpdir/writeenc.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-writeenc.Tpo $(DEPDIR)/liboldmpost_a-writeenc.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/writeenc.c' object='liboldmpost_a-writeenc.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-writeenc.o `test -f 'mpdir/writeenc.c' || echo '$(srcdir)/'`mpdir/writeenc.c
-
-liboldmpost_a-writeenc.obj: mpdir/writeenc.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-writeenc.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-writeenc.Tpo -c -o liboldmpost_a-writeenc.obj `if test -f 'mpdir/writeenc.c'; then $(CYGPATH_W) 'mpdir/writeenc.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/writeenc.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-writeenc.Tpo $(DEPDIR)/liboldmpost_a-writeenc.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/writeenc.c' object='liboldmpost_a-writeenc.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-writeenc.obj `if test -f 'mpdir/writeenc.c'; then $(CYGPATH_W) 'mpdir/writeenc.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/writeenc.c'; fi`
-
-liboldmpost_a-writefont.o: mpdir/writefont.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-writefont.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-writefont.Tpo -c -o liboldmpost_a-writefont.o `test -f 'mpdir/writefont.c' || echo '$(srcdir)/'`mpdir/writefont.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-writefont.Tpo $(DEPDIR)/liboldmpost_a-writefont.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/writefont.c' object='liboldmpost_a-writefont.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-writefont.o `test -f 'mpdir/writefont.c' || echo '$(srcdir)/'`mpdir/writefont.c
-
-liboldmpost_a-writefont.obj: mpdir/writefont.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-writefont.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-writefont.Tpo -c -o liboldmpost_a-writefont.obj `if test -f 'mpdir/writefont.c'; then $(CYGPATH_W) 'mpdir/writefont.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/writefont.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-writefont.Tpo $(DEPDIR)/liboldmpost_a-writefont.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/writefont.c' object='liboldmpost_a-writefont.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-writefont.obj `if test -f 'mpdir/writefont.c'; then $(CYGPATH_W) 'mpdir/writefont.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/writefont.c'; fi`
-
-liboldmpost_a-writet1.o: mpdir/writet1.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-writet1.o -MD -MP -MF $(DEPDIR)/liboldmpost_a-writet1.Tpo -c -o liboldmpost_a-writet1.o `test -f 'mpdir/writet1.c' || echo '$(srcdir)/'`mpdir/writet1.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-writet1.Tpo $(DEPDIR)/liboldmpost_a-writet1.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/writet1.c' object='liboldmpost_a-writet1.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-writet1.o `test -f 'mpdir/writet1.c' || echo '$(srcdir)/'`mpdir/writet1.c
-
-liboldmpost_a-writet1.obj: mpdir/writet1.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT liboldmpost_a-writet1.obj -MD -MP -MF $(DEPDIR)/liboldmpost_a-writet1.Tpo -c -o liboldmpost_a-writet1.obj `if test -f 'mpdir/writet1.c'; then $(CYGPATH_W) 'mpdir/writet1.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/writet1.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/liboldmpost_a-writet1.Tpo $(DEPDIR)/liboldmpost_a-writet1.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpdir/writet1.c' object='liboldmpost_a-writet1.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(liboldmpost_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o liboldmpost_a-writet1.obj `if test -f 'mpdir/writet1.c'; then $(CYGPATH_W) 'mpdir/writet1.c'; else $(CYGPATH_W) '$(srcdir)/mpdir/writet1.c'; fi`
-
libpdftex_a-avl.o: pdftexdir/avl.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libpdftex_a_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT libpdftex_a-avl.o -MD -MP -MF $(DEPDIR)/libpdftex_a-avl.Tpo -c -o libpdftex_a-avl.o `test -f 'pdftexdir/avl.c' || echo '$(srcdir)/'`pdftexdir/avl.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/libpdftex_a-avl.Tpo $(DEPDIR)/libpdftex_a-avl.Po
@@ -6637,90 +6456,6 @@ mpost-mpxout.obj: mpxout.c
@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(mpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mpost-mpxout.obj `if test -f 'mpxout.c'; then $(CYGPATH_W) 'mpxout.c'; else $(CYGPATH_W) '$(srcdir)/mpxout.c'; fi`
-oldmpost-mpini.o: mpini.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mpini.o -MD -MP -MF $(DEPDIR)/oldmpost-mpini.Tpo -c -o oldmpost-mpini.o `test -f 'mpini.c' || echo '$(srcdir)/'`mpini.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mpini.Tpo $(DEPDIR)/oldmpost-mpini.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpini.c' object='oldmpost-mpini.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mpini.o `test -f 'mpini.c' || echo '$(srcdir)/'`mpini.c
-
-oldmpost-mpini.obj: mpini.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mpini.obj -MD -MP -MF $(DEPDIR)/oldmpost-mpini.Tpo -c -o oldmpost-mpini.obj `if test -f 'mpini.c'; then $(CYGPATH_W) 'mpini.c'; else $(CYGPATH_W) '$(srcdir)/mpini.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mpini.Tpo $(DEPDIR)/oldmpost-mpini.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpini.c' object='oldmpost-mpini.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mpini.obj `if test -f 'mpini.c'; then $(CYGPATH_W) 'mpini.c'; else $(CYGPATH_W) '$(srcdir)/mpini.c'; fi`
-
-oldmpost-mp0.o: mp0.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp0.o -MD -MP -MF $(DEPDIR)/oldmpost-mp0.Tpo -c -o oldmpost-mp0.o `test -f 'mp0.c' || echo '$(srcdir)/'`mp0.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp0.Tpo $(DEPDIR)/oldmpost-mp0.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp0.c' object='oldmpost-mp0.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp0.o `test -f 'mp0.c' || echo '$(srcdir)/'`mp0.c
-
-oldmpost-mp0.obj: mp0.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp0.obj -MD -MP -MF $(DEPDIR)/oldmpost-mp0.Tpo -c -o oldmpost-mp0.obj `if test -f 'mp0.c'; then $(CYGPATH_W) 'mp0.c'; else $(CYGPATH_W) '$(srcdir)/mp0.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp0.Tpo $(DEPDIR)/oldmpost-mp0.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp0.c' object='oldmpost-mp0.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp0.obj `if test -f 'mp0.c'; then $(CYGPATH_W) 'mp0.c'; else $(CYGPATH_W) '$(srcdir)/mp0.c'; fi`
-
-oldmpost-mp1.o: mp1.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp1.o -MD -MP -MF $(DEPDIR)/oldmpost-mp1.Tpo -c -o oldmpost-mp1.o `test -f 'mp1.c' || echo '$(srcdir)/'`mp1.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp1.Tpo $(DEPDIR)/oldmpost-mp1.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp1.c' object='oldmpost-mp1.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp1.o `test -f 'mp1.c' || echo '$(srcdir)/'`mp1.c
-
-oldmpost-mp1.obj: mp1.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp1.obj -MD -MP -MF $(DEPDIR)/oldmpost-mp1.Tpo -c -o oldmpost-mp1.obj `if test -f 'mp1.c'; then $(CYGPATH_W) 'mp1.c'; else $(CYGPATH_W) '$(srcdir)/mp1.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp1.Tpo $(DEPDIR)/oldmpost-mp1.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp1.c' object='oldmpost-mp1.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp1.obj `if test -f 'mp1.c'; then $(CYGPATH_W) 'mp1.c'; else $(CYGPATH_W) '$(srcdir)/mp1.c'; fi`
-
-oldmpost-mp2.o: mp2.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp2.o -MD -MP -MF $(DEPDIR)/oldmpost-mp2.Tpo -c -o oldmpost-mp2.o `test -f 'mp2.c' || echo '$(srcdir)/'`mp2.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp2.Tpo $(DEPDIR)/oldmpost-mp2.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp2.c' object='oldmpost-mp2.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp2.o `test -f 'mp2.c' || echo '$(srcdir)/'`mp2.c
-
-oldmpost-mp2.obj: mp2.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp2.obj -MD -MP -MF $(DEPDIR)/oldmpost-mp2.Tpo -c -o oldmpost-mp2.obj `if test -f 'mp2.c'; then $(CYGPATH_W) 'mp2.c'; else $(CYGPATH_W) '$(srcdir)/mp2.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp2.Tpo $(DEPDIR)/oldmpost-mp2.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp2.c' object='oldmpost-mp2.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp2.obj `if test -f 'mp2.c'; then $(CYGPATH_W) 'mp2.c'; else $(CYGPATH_W) '$(srcdir)/mp2.c'; fi`
-
-oldmpost-mp-pool.o: mp-pool.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp-pool.o -MD -MP -MF $(DEPDIR)/oldmpost-mp-pool.Tpo -c -o oldmpost-mp-pool.o `test -f 'mp-pool.c' || echo '$(srcdir)/'`mp-pool.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp-pool.Tpo $(DEPDIR)/oldmpost-mp-pool.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp-pool.c' object='oldmpost-mp-pool.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp-pool.o `test -f 'mp-pool.c' || echo '$(srcdir)/'`mp-pool.c
-
-oldmpost-mp-pool.obj: mp-pool.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mp-pool.obj -MD -MP -MF $(DEPDIR)/oldmpost-mp-pool.Tpo -c -o oldmpost-mp-pool.obj `if test -f 'mp-pool.c'; then $(CYGPATH_W) 'mp-pool.c'; else $(CYGPATH_W) '$(srcdir)/mp-pool.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mp-pool.Tpo $(DEPDIR)/oldmpost-mp-pool.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mp-pool.c' object='oldmpost-mp-pool.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mp-pool.obj `if test -f 'mp-pool.c'; then $(CYGPATH_W) 'mp-pool.c'; else $(CYGPATH_W) '$(srcdir)/mp-pool.c'; fi`
-
-oldmpost-mpextra.o: mpextra.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mpextra.o -MD -MP -MF $(DEPDIR)/oldmpost-mpextra.Tpo -c -o oldmpost-mpextra.o `test -f 'mpextra.c' || echo '$(srcdir)/'`mpextra.c
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mpextra.Tpo $(DEPDIR)/oldmpost-mpextra.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpextra.c' object='oldmpost-mpextra.o' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mpextra.o `test -f 'mpextra.c' || echo '$(srcdir)/'`mpextra.c
-
-oldmpost-mpextra.obj: mpextra.c
-@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT oldmpost-mpextra.obj -MD -MP -MF $(DEPDIR)/oldmpost-mpextra.Tpo -c -o oldmpost-mpextra.obj `if test -f 'mpextra.c'; then $(CYGPATH_W) 'mpextra.c'; else $(CYGPATH_W) '$(srcdir)/mpextra.c'; fi`
-@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/oldmpost-mpextra.Tpo $(DEPDIR)/oldmpost-mpextra.Po
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='mpextra.c' object='oldmpost-mpextra.obj' libtool=no @AMDEPBACKSLASH@
-@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
-@am__fastdepCC_FALSE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(oldmpost_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o oldmpost-mpextra.obj `if test -f 'mpextra.c'; then $(CYGPATH_W) 'mpextra.c'; else $(CYGPATH_W) '$(srcdir)/mpextra.c'; fi`
-
pdftex-pdftexini.o: pdftexini.c
@am__fastdepCC_TRUE@ $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(pdftex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT pdftex-pdftexini.o -MD -MP -MF $(DEPDIR)/pdftex-pdftexini.Tpo -c -o pdftex-pdftexini.o `test -f 'pdftexini.c' || echo '$(srcdir)/'`pdftexini.c
@am__fastdepCC_TRUE@ $(am__mv) $(DEPDIR)/pdftex-pdftexini.Tpo $(DEPDIR)/pdftex-pdftexini.Po
@@ -8188,13 +7923,6 @@ dvicopy-web2c: $(web2c_depend) dvicopy.p
touch dvicopy.c dvicopy.h
dvicopy.p: tangle$(EXEEXT) dvicopy.web dvicopy.ch
$(tangle) dvicopy dvicopy
-dvitomp.c dvitomp.h: dvitomp-web2c
-dvitomp-web2c: $(web2c_depend) dvitomp.p
- $(web2c) dvitomp
- echo timestamp >$@
- touch dvitomp.c dvitomp.h
-dvitomp.p: tangle$(EXEEXT) dvitomp.web dvitomp.ch
- $(tangle) dvitomp dvitomp
dvitype.c dvitype.h: dvitype-web2c
dvitype-web2c: $(web2c_depend) dvitype.p
$(web2c) dvitype
@@ -8368,23 +8096,6 @@ trap-clean:
rm -f mftrapin.fot mftrapin.log
rm -f mftrap.fot mftrap.log mftrap.tfm
rm -f mftrap.pl trap.72270gf trap.typ
-$(oldmpost_c_h): mp-web2c
-mp-web2c: mp.p $(web2c_texmf) mpdir/mp.defines web2c/cvtmf1.sed web2c/cvtmf2.sed
- $(web2c) mp
- echo timestamp >$@
- touch $(oldmpost_c_h)
-mpextra.c: lib/texmfmp.c mpd.h
- sed s/TEX-OR-MF-OR-MP/mp/ $(srcdir)/lib/texmfmp.c >$@
-mp.p mp.pool: mp-tangle
-mp-tangle: tangle$(EXEEXT) mp.web mp.ch
- $(tangle) mp mp
- echo timestamp >$@
- touch mp.p mp.pool
-mp-pool.c: mp.pool $(makecpool_stamp) mpdir/mplib.h
- $(makecpool) mp.pool $(srcdir)/mpdir/mplib.h >$@ || rm -f $@
-
-# We must create mpd.h before building the liboldmpost_a_OBJECTS.
-$(liboldmpost_a_OBJECTS): mpd.h
mpost.c: ctangle$(EXEEXT) mplibdir/mpost.w
$(mp_ctangle) mpost.w
diff --git a/Build/source/texk/web2c/am/web.am b/Build/source/texk/web2c/am/web.am
index 6f9dc8f8f45..12ca5a72739 100644
--- a/Build/source/texk/web2c/am/web.am
+++ b/Build/source/texk/web2c/am/web.am
@@ -3,7 +3,7 @@
## Copyright (C) 2009 Peter Breitenlohner <tex-live@tug.org>
## You may freely use, modify and/or distribute this file.
-web_programs = bibtex dvicopy dvitomp dvitype gftodvi gftopk gftype mft \
+web_programs = bibtex dvicopy dvitype gftodvi gftopk gftype mft \
patgen pktogf pktype pltotf pooltype tftopl vftovp vptovf weave
if WEB
@@ -33,15 +33,6 @@ dvicopy-web2c: $(web2c_depend) dvicopy.p
dvicopy.p: tangle$(EXEEXT) dvicopy.web dvicopy.ch
$(tangle) dvicopy dvicopy
-nodist_dvitomp_SOURCES = dvitomp.c dvitomp.h
-dvitomp.c dvitomp.h: dvitomp-web2c
-dvitomp-web2c: $(web2c_depend) dvitomp.p
- $(web2c) dvitomp
- echo timestamp >$@
- touch dvitomp.c dvitomp.h
-dvitomp.p: tangle$(EXEEXT) dvitomp.web dvitomp.ch
- $(tangle) dvitomp dvitomp
-
nodist_dvitype_SOURCES = dvitype.c dvitype.h
dvitype.c dvitype.h: dvitype-web2c
dvitype-web2c: $(web2c_depend) dvitype.p
@@ -184,8 +175,6 @@ endif WEB
DISTCLEANFILES += tests/xexampl.aux tests/xexampl.bbl tests/xexampl.blg
## dvicopy.test
DISTCLEANFILES += tests/xstory.dvi tests/xpplr.dvi
-## dvitomp.test
-DISTCLEANFILES += ptmr.mpx tests/xptmr.mpx tests/xstory.mpx
## dvitype.test
DISTCLEANFILES += tests/xstory.dvityp tests/xpagenum.typ
## gftodvi.test
diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure
index 3a3026d240b..442d6f60122 100755
--- a/Build/source/texk/web2c/configure
+++ b/Build/source/texk/web2c/configure
@@ -24705,7 +24705,7 @@ fi
subdirs="$subdirs web2c"
-ac_config_files="$ac_config_files Makefile doc/Makefile lib/Makefile man/Makefile mpware/Makefile omegafonts/Makefile otps/Makefile window/Makefile"
+ac_config_files="$ac_config_files Makefile doc/Makefile lib/Makefile man/Makefile omegafonts/Makefile otps/Makefile window/Makefile"
@@ -25881,7 +25881,6 @@ do
"doc/Makefile") CONFIG_FILES="$CONFIG_FILES doc/Makefile" ;;
"lib/Makefile") CONFIG_FILES="$CONFIG_FILES lib/Makefile" ;;
"man/Makefile") CONFIG_FILES="$CONFIG_FILES man/Makefile" ;;
- "mpware/Makefile") CONFIG_FILES="$CONFIG_FILES mpware/Makefile" ;;
"omegafonts/Makefile") CONFIG_FILES="$CONFIG_FILES omegafonts/Makefile" ;;
"otps/Makefile") CONFIG_FILES="$CONFIG_FILES otps/Makefile" ;;
"window/Makefile") CONFIG_FILES="$CONFIG_FILES window/Makefile" ;;
diff --git a/Build/source/texk/web2c/configure.ac b/Build/source/texk/web2c/configure.ac
index c073f774413..bd50318ab8e 100644
--- a/Build/source/texk/web2c/configure.ac
+++ b/Build/source/texk/web2c/configure.ac
@@ -348,8 +348,7 @@ fi
AC_CONFIG_SUBDIRS([web2c])
AC_CONFIG_FILES([Makefile doc/Makefile lib/Makefile man/Makefile
- mpware/Makefile omegafonts/Makefile otps/Makefile
- window/Makefile])
+ omegafonts/Makefile otps/Makefile window/Makefile])
dnl The subdirectory web2c must be configured for the build system.
dnl Can not share the cache file with the subdirectory!
diff --git a/Build/source/texk/web2c/dvitomp.ch b/Build/source/texk/web2c/dvitomp.ch
deleted file mode 100644
index b3393dfc2fe..00000000000
--- a/Build/source/texk/web2c/dvitomp.ch
+++ /dev/null
@@ -1,837 +0,0 @@
-% $Id: dvitomp.ch,v 1.7 2005/04/28 06:45:21 taco Exp $
-% dvitomp.ch for C compilation with web2c. Public domain.
-%
-% Change file for the DVItoMP processor, for use with WEB to C
-% This file was created by John Hobby. It is loosely based on the
-% change file for the WEB to C version of dvitype (due to Howard
-% Trickey and Pavel Curtis).
-%
-% 3/11/90 (JDH) Original version.
-% 4/30/90 (JDH) Update to handle virtual fonts
-% 4/16/93 (JDH) Make output go to standard output and require mpx file
-% to be a command line argument.
-%
-% 1/18/95 (UV) Update based on dvitype.ch for web2c-6.1
-% 4/13/95 (UV) Cosmetic changes for release of web2c-mp
-% 10/08/95 (UV) Bug fix: need to replace abs() with floating-point arg
-% by fabs() because of different definition in cpascal.h
-% as reported by Dane Dwyer <dwyer@geisel.csl.uiuc.edu>.
-
-@x [0] WEAVE: print changes only.
-\pageno=\contentspagenumber \advance\pageno by 1
-@y
-\pageno=\contentspagenumber \advance\pageno by 1
-\let\maybe=\iffalse
-\def\title{DVI$\,$\lowercase{to}MP changes for C}
-@z
-
-@x [1] Duplicate banner line for use in |print_version_and_exit|.
-@d banner=='% Written by DVItoMP, Version 1.003'
- {the first line of the output file}
-@y
-@d banner=='% Written by DVItoMP, Version 1.003/color'
- {the first line of the output file}
-@d term_banner=='This is DVItoMP, Version 1.003/color'
- {the same in the usual format, as it would be shown on a terminal}
-@z
-
-@x [3] Set up kpathsea.
-procedure initialize; {this procedure gets things started properly}
- var i:integer; {loop index for initializations}
- begin @<Set initial values@>@/
-@y
-@<Define |parse_arguments|@>
-procedure initialize; {this procedure gets things started properly}
- var i:integer; {loop index for initializations}
- begin
- kpse_set_progname (argv[0]); {initialize for the filename searches}
- parse_arguments;
- @<Set initial values@>@/
-@z
-
-@x [7] Remove non-local goto.
-@d abort(#)==begin err_print_ln('DVItoMP abort: ',#);
- history:=fatal_error; jump_out;
- end
-@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
-@.Bad DVI file@>
-@d warn(#)==begin err_print_ln('DVItoMP warning: ',#);
- history:=warning_given;
- end
-
-@p procedure jump_out;
-begin goto final_end;
-end;
-@y
-@d jump_out==uexit(history)
-@d abort(#)==begin err_print_ln('DVItoMP abort: ',#);
- history:=fatal_error; jump_out;
- end
-@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
-@.Bad DVI file@>
-@d warn(#)==begin err_print_ln('DVItoMP warning: ',#);
- history:=warning_given;
- end
-@z
-
-@x [11] Permissive input.
-@!ASCII_code=" ".."~"; {a subrange of the integers}
-@y
-@!ASCII_code=0..255; {a subrange of the integers}
-@z
-
-% [12] The text_char type is used as an array index into `xord'. The
-% default type `char' produces signed integers, which are bad array
-% indices in C.
-@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|}
-@y
-@d text_char == ASCII_code {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|}
-@z
-
-@x [14] Fix up opening the files.
-@p procedure open_mpx_file; {prepares to write text on |mpx_file|}
-begin rewrite(mpx_file);
-end;
-@y
-@p procedure open_mpx_file; {prepares to write text on |mpx_file|}
-begin
- cur_name := extend_filename (mpx_name, 'mpx');
- rewrite (mpx_file, cur_name);
-end;
-@z
-
-@x [19] More file opening.
-@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
-begin reset(dvi_file);
-if eof(dvi_file) then abort('DVI file not found');
-end;
-@#
-function open_tfm_file:boolean; {prepares to read packed bytes in |tfm_file|}
-begin reset(tfm_file,cur_name);
-open_tfm_file:=(not eof(tfm_file));
-end;
-@#
-function open_vf_file:boolean; {prepares to read packed bytes in |vf_file|}
-begin reset(vf_file,cur_name);
-open_vf_file:=(not eof(vf_file));
-end;
-@y
-@p procedure open_dvi_file; {prepares to read packed bytes in |dvi_file|}
-begin
- cur_name := extend_filename (dvi_name, 'dvi');
- resetbin(dvi_file, cur_name);
-end;
-@#
-function open_tfm_file:boolean; {prepares to read packed bytes in |tfm_file|}
-begin
- tfm_file := kpse_open_file (cur_name, kpse_tfm_format);
- free (cur_name); {We |xmalloc|'d this before we got called.}
- open_tfm_file := true; {If we get here, we succeeded.}
-end;
-@#
-function open_vf_file:boolean; {prepares to read packed bytes in |tfm_file|}
-var @!full_name:^char;
-begin
- {It's ok if the \.{VF} file doesn't exist.}
- full_name := kpse_find_vf (cur_name);
- if full_name then begin
- resetbin (vf_file, full_name);
- free (cur_name);
- free (full_name);
- open_vf_file := true;
- end else
- open_vf_file := false;
-end;
-@z
-
-@x [24] No arbitrary limit on filename length.
-@!cur_name:packed array[1..name_length] of char; {external name,
- with no lower case letters}
-@y
-@!cur_name:^char; {external name}
-@z
-
-@x [26] Make get_n_bytes routines work with 16-bit math.
-get_two_bytes:=a*256+b;
-@y
-get_two_bytes:=a*intcast(256)+b;
-@z
-@x
-get_three_bytes:=(a*256+b)*256+c;
-@y
-get_three_bytes:=(a*intcast(256)+b)*256+c;
-@z
-@x
-if a<128 then signed_trio:=(a*256+b)*256+c
-else signed_trio:=((a-256)*256+b)*256+c;
-@y
-if a<128 then signed_trio:=(a*intcast(256)+b)*256+c
-else signed_trio:=((a-intcast(256))*256+b)*256+c;
-@z
-@x
-if a<128 then signed_quad:=((a*256+b)*256+c)*256+d
-else signed_quad:=(((a-256)*256+b)*256+c)*256+d;
-@y
-if a<128 then signed_quad:=((a*intcast(256)+b)*256+c)*256+d
-else signed_quad:=(((a-256)*intcast(256)+b)*256+c)*256+d;
-@z
-
-@x [32] l.672 Bugfix: local_only is indexed by internal font numbers.
-@!local_only:array [0..max_fonts] of boolean; {|font_num| meaningless?}
-@y
-@!local_only:array [0..max_fnums] of boolean; {|font_num| meaningless?}
-@z
-
-@x [41] Fix abs() with floating-point arg.
- begin if abs(font_scaled_size[f]-font_scaled_size[ff])
-@y
- begin if fabs(font_scaled_size[f]-font_scaled_size[ff])
-@z
-
-@x [43] Fix abs() with floating-point arg.
-if abs(font_design_size[f]-font_design_size[ff]) > font_tolerance then
-@y
-if fabs(font_design_size[f]-font_design_size[ff]) > font_tolerance then
-@z
-
-@x [43] Checksum warning: set history to cksum_trouble, not warning_given.
- font_warn('Checksum mismatch for ')(ff)
-@.Checksum mismatch@>
-@y
- begin err_print('DVItoMP warning: Checksum mismatch for ');
-@.Checksum mismatch@>
- err_print_font(ff);
- if history=spotless then history:=cksum_trouble;
- end
-@z
-
-@x [46] Make 16-bit TFM calculations work.
-read_tfm_word; lh:=b2*256+b3;
-read_tfm_word; font_bc[f]:=b0*256+b1; font_ec[f]:=b2*256+b3;
-@y
-read_tfm_word; lh:=b2*intcast(256)+b3;
-read_tfm_word; font_bc[f]:=b0*intcast(256)+b1; font_ec[f]:=b2*intcast(256)+b3;
-@z
-@x
- 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;
-@y
- if b0<128 then tfm_check_sum:=((b0*intcast(256)+b1)*256+b2)*256+b3
- else tfm_check_sum:=(((b0-256)*intcast(256)+b1)*256+b2)*256+b3;
-@z
-
-% For the code to work, Hobby notes that we need |round|(|x| - 0.5)
-% to be equal to $\lfloor x\rfloor$. For Pascal's |round| function
-% this is emphatically not true.
-@x [49] We need floor, not round.
-round(dvi_scale*font_scaled_size[f]*char_width(f)(c)-0.5)
-@y
-floor(dvi_scale*font_scaled_size[f]*char_width(f)(c))
-@z
-
-@x [50] We need floor, not round.
-round(dvi_scale*font_scaled_size[cur_font]*char_width(cur_font)(p)-0.5)
-@y
-floor(dvi_scale*font_scaled_size[cur_font]*char_width(cur_font)(p))
-@z
-
-% [61] Don't 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
-There is no single |default_directory| with C.
-@z
-
-@x [62] Remove initialization of default_directory.
-@ @<Set init...@>=
-default_directory:=default_directory_name;
-@y
-@ (No initialization needs to be done. Keep this module to preserve
-numbering.)
-@z
-
-@x [63] Dynamically allocate cur_name, don't add .vf.
-for k:=1 to name_length do cur_name[k]:=' ';
-if area_length[f]=0 then
- begin for k:=1 to default_directory_name_length do
- cur_name[k]:=default_directory[k];
- l:=default_directory_name_length;
- end
-else l:=0;
-for k:=font_name[f] to font_name[f+1]-1 do
- begin incr(l);
- if l+3>name_length then
- abort('DVItoMP capacity exceeded (max font name length=',
- name_length:1,')!');
-@.DVItoMP capacity exceeded...@>
- if (names[k]>="a")and(names[k]<="z") then
- cur_name[l]:=xchr[names[k]-@'40]
- else cur_name[l]:=xchr[names[k]];
- end;
-cur_name[l+1]:='.'; cur_name[l+2]:='V'; cur_name[l+3]:='F'
-@y
-{This amounts to a string copy. }
-cur_name := xmalloc_array (char, font_name[f+1] - font_name[f]);
-for k:=font_name[f] to font_name[f+1]-1 do begin
- cur_name[k - font_name[f]] := xchr[names[k]];
-end;
-cur_name[font_name[f+1] - font_name[f]] := 0;
-@z
-
-@x [64] Since we didn't add .vf, don't need to change it to .tfm.
-l:=area_length[f];
-if l=0 then l:=default_directory_name_length;
-l:=l+font_name[f+1]-font_name[f];
-if l+4>name_length then
- abort('DVItoMP capacity exceeded (max font name length=',
- name_length:1,')!');
-@.DVItoMP capacity exceeded...@>
-cur_name[l+2]:='T'; cur_name[l+3]:='F'; cur_name[l+4]:='M'
-@y
-do_nothing
-@z
-
-@x [75] Handle colored text. (COLOR)
-print_ln('vardef _s(expr _t,_f,_m,_x,_y)=');
-print_ln(' addto _p also _t infont _f scaled _m shifted (_x,_y); enddef;');
-@y
-print_ln('vardef _s(expr _t,_f,_m,_x,_y)(text _c)=');
-print_ln(' addto _p also _t infont _f scaled _m shifted (_x,_y) _c; enddef;');
-@z
-
-@x [78] Fix printing of real numbers, and add color (COLOR).
- if (abs(x)>=4096.0)or(abs(y)>=4096.0)or(m>=4096.0)or(m<0) then
- begin warn('text scaled ',m:1:1,@|
- ' at (',x:1:1,',',y:1:1,') is out of range');
- end_char_string(60);
- end
- else end_char_string(40);
- print_ln(',_n',str_f:1,',',m:1:5,',',x:1:4,',',y:1:4,');');
-@y
- if (fabs(x)>=4096.0)or(fabs(y)>=4096.0)or(m>=4096.0)or(m<0) then
- begin warn('text is out of range');
- end_char_string(60);
- end
- else end_char_string(40);
- print(',_n',str_f:1,',');
- fprint_real(mpx_file, m,1,5); print(',');
- fprint_real(mpx_file, x,1,4); print(',');
- fprint_real(mpx_file, y,1,4); print(',');@/
- @<Print a \.{withcolor} specifier if appropriate@>@/
- print_ln(');');
-@z
-
-@x [79] Fix _r definition (COLOR).
- print_ln('vardef _r(expr _a,_w) =');
- print_ln(' addto _p doublepath _a withpen pencircle scaled _w enddef;');
-@y
- print_ln('vardef _r(expr _a,_w)(text _t) =');
- print_ln(' addto _p doublepath _a withpen pencircle scaled _w _t enddef;');
-@z
-
-@x [79] Another fix for printing of real numbers, plus colors (COLOR).
- if (abs(xx1)>=4096.0)or(abs(yy1)>=4096.0)or@|
- (abs(xx2)>=4096.0)or(abs(yy2)>=4096.0)or(ww>=4096.0) then
- warn('hrule or vrule near (',xx1:1:1,',',yy1:1:1,') is out of range');
- print_ln('_r((',xx1:1:4,',',yy1:1:4,')..(',xx2:1:4,',',yy2:1:4,
- '), ',ww:1:4,');');
-@y
- if (fabs(xx1)>=4096.0)or(fabs(yy1)>=4096.0)or@|
- (fabs(xx2)>=4096.0)or(fabs(yy2)>=4096.0)or(ww>=4096.0) then
- warn('hrule or vrule is out of range');
- print('_r((');
- fprint_real(mpx_file, xx1,1,4); print(',');
- fprint_real(mpx_file, yy1,1,4); print(')..(');
- fprint_real(mpx_file, xx2,1,4); print(',');
- fprint_real(mpx_file, yy2,1,4); print('), ');
- fprint_real(mpx_file, ww,1,4); print(',');
- @<Print a \.{withcolor} specifier if appropriate@>@/
- print_ln(');');
-@z
-
-@x [80] Yet another fix for printing of real numbers.
-print_ln('setbounds _p to (0,',dd:1:4,')--(',w:1:4,',',dd:1:4,')--');
-print_ln(' (',w:1:4,',',h:1:4,')--(0,',h:1:4,')--cycle;')
-@y
-print('setbounds _p to (0,');
-fprint_real(mpx_file, dd,1,4); print(')--(');
-fprint_real(mpx_file, w,1,4); print(',');
-fprint_real(mpx_file, dd,1,4); print_ln(')--');@/
-print(' (');
-fprint_real(mpx_file, w,1,4); print(',');
-fprint_real(mpx_file, h,1,4); print(')--(0,');
-fprint_real(mpx_file, h,1,4); print_ln(')--cycle;')
-@z
-
-@x [88] push and pop commands (COLOR).
-@p procedure do_push;
-@y
-@p @<Declare procedures to handle color commands@>
-procedure do_push;
-@z
-
-@x [94] Additional cases for DVI commands (COLOR).
-four_cases(xxx1): for k:=1 to p do
- down_the_drain:=get_byte;
-@y
-four_cases(xxx1): do_xxx(p);
-@z
-
-@x [98] Main program.
-print_ln(banner);
-@y
-print (banner);
-print_ln (version_string);
-@z
-@x Exit with appropriate status.
-final_end:end.
-@y
-if history<=cksum_trouble then uexit(0)
-else uexit(history);
-end.
-@z
-
-@x [103] System-dependent changes.
-This section should be replaced, if necessary, by changes to the program
-that are necessary to make \.{DVItoMP} 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
-Parse a Unix-style command line.
-
-@d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0)
-
-@<Define |parse_arguments|@> =
-procedure parse_arguments;
-const n_options = 2; {Pascal won't count array lengths for us.}
-var @!long_options: array[0..n_options] of getopt_struct;
- @!getopt_return_val: integer;
- @!option_index: c_int_type;
- @!current_option: 0..n_options;
-begin
- @<Define the option table@>;
- repeat
- getopt_return_val := getopt_long_only (argc, argv, '', long_options,
- address_of (option_index));
- if getopt_return_val = -1 then begin
- {End of arguments; we exit the loop below.} ;
-
- end else if getopt_return_val = "?" then begin
- usage ('dvitomp');
-
- end else if argument_is ('help') then begin
- usage_help (DVITOMP_HELP, nil);
-
- end else if argument_is ('version') then begin
- print_version_and_exit (term_banner, 'AT&T Bell Laboraties', 'John Hobby.', nil);
-
- end; {Else it was a flag; |getopt| has already done the assignment.}
- until getopt_return_val = -1;
-
- {Now |optind| is the index of first non-option on the command line.
- We must have one or two remaining arguments.}
- if (optind + 1 <> argc) and (optind + 2 <> argc) then begin
- write_ln (stderr, 'dvitomp: Need one or two file arguments.');
- usage ('dvitomp');
- end;
-
- dvi_name := cmdline (optind);
-
- if optind + 2 <= argc then begin
- mpx_name := cmdline (optind + 1); {The user specified the other name.}
- end else begin
- {User did not specify the other name; default it from the first.}
- mpx_name := basename_change_suffix (dvi_name, '.dvi', '.mpx');
- end;
-end;
-
-@ Here are the options we allow. The first is one of the standard GNU options.
-@.-help@>
-
-@<Define the option...@> =
-current_option := 0;
-long_options[current_option].name := 'help';
-long_options[current_option].has_arg := 0;
-long_options[current_option].flag := 0;
-long_options[current_option].val := 0;
-incr (current_option);
-
-@ Another of the standard options.
-@.-version@>
-
-@<Define the option...@> =
-long_options[current_option].name := 'version';
-long_options[current_option].has_arg := 0;
-long_options[current_option].flag := 0;
-long_options[current_option].val := 0;
-incr (current_option);
-
-@ An element with all zeros always ends the list.
-
-@<Define the option...@> =
-long_options[current_option].name := 0;
-long_options[current_option].has_arg := 0;
-long_options[current_option].flag := 0;
-long_options[current_option].val := 0;
-
-@ Global filenames.
-
-@<Global...@> =
-@!dvi_name, @!mpx_name:c_string;
-
-
-
-@* Color support.
-These changes support \.{dvips}-style ``\.{color push NAME}'' and
-``\.{color pop}'' specials. We store a list of named colors, sorted by
-name, and decorate the relevant drawing commands with ``\.{withcolor
-(r,g,b)}'' specifiers while a color is defined.
-
-@ A constant bounding the size of the named-color array.
-
-@<Constants...@> =
-@!max_named_colors=100; {maximum number of distinct named colors}
-
-@ Then we declare a record for color types.
-
-@<Types...@> =
-@!named_color_record=record@;@/
- @!name:c_string; {color name}
- @!value:c_string; {text to pass to MetaPost}
- end;
-
-@ Declare the named-color array itself.
-
-@<Globals...@> =
-@!named_colors: array[1..max_named_colors] of named_color_record;
- {stores information about named colors, in sorted order by name}
-@!num_named_colors:integer; {number of elements of |named_colors| that are valid}
-
-@ This function, used only during initialization, defines a named color.
-
-@<Define |parse_arguments|@> =
-procedure def_named_color(n,v: c_string);
- begin
- if num_named_colors = max_named_colors then
- abort('too many named color definitions')
- else if (num_named_colors > 0)
- and (strcmp(n, named_colors[num_named_colors].name) <= 0)
- then
- abort('named colors added out of alphabetical order');
- incr(num_named_colors);
- named_colors[num_named_colors].name := n;
- named_colors[num_named_colors].value := v
- end;
-
-@ During the initialization phase, we define values for all the named
-colors defined in \.{colordvi.tex}. CMYK-to-RGB conversion by GhostScript.
-
-@<Set initial values@> =
-num_named_colors := 0;
-def_named_color('Apricot', '(1.0, 0.680006, 0.480006)');
-def_named_color('Aquamarine', '(0.180006, 1.0, 0.7)');
-def_named_color('Bittersweet', '(0.760012, 0.0100122, 0.0)');
-def_named_color('Black', '(0.0, 0.0, 0.0)');
-def_named_color('Blue', '(0.0, 0.0, 1.0)');
-def_named_color('BlueGreen', '(0.15, 1.0, 0.669994)');
-def_named_color('BlueViolet', '(0.1, 0.05, 0.960012)');
-def_named_color('BrickRed', '(0.719994, 0.0, 0.0)');
-def_named_color('Brown', '(0.4, 0.0, 0.0)');
-def_named_color('BurntOrange', '(1.0, 0.489988, 0.0)');
-def_named_color('CadetBlue', '(0.380006, 0.430006, 0.769994)');
-def_named_color('CarnationPink', '(1.0, 0.369994, 1.0)');
-def_named_color('Cerulean', '(0.0600122, 0.889988, 1.0)');
-def_named_color('CornflowerBlue', '(0.35, 0.869994, 1.0)');
-def_named_color('Cyan', '(0.0, 1.0, 1.0)');
-def_named_color('Dandelion', '(1.0, 0.710012, 0.160012)');
-def_named_color('DarkOrchid', '(0.6, 0.2, 0.8)');
-def_named_color('Emerald', '(0.0, 1.0, 0.5)');
-def_named_color('ForestGreen', '(0.0, 0.880006, 0.0)');
-def_named_color('Fuchsia', '(0.45, 0.00998169, 0.919994)');
-def_named_color('Goldenrod', '(1.0, 0.9, 0.160012)');
-def_named_color('Gray', '(0.5, 0.5, 0.5)');
-def_named_color('Green', '(0.0, 1.0, 0.0)');
-def_named_color('GreenYellow', '(0.85, 1.0, 0.310012)');
-def_named_color('JungleGreen', '(0.0100122, 1.0, 0.480006)');
-def_named_color('Lavender', '(1.0, 0.519994, 1.0)');
-def_named_color('LimeGreen', '(0.5, 1.0, 0.0)');
-def_named_color('Magenta', '(1.0, 0.0, 1.0)');
-def_named_color('Mahogany', '(0.65, 0.0, 0.0)');
-def_named_color('Maroon', '(0.680006, 0.0, 0.0)');
-def_named_color('Melon', '(1.0, 0.539988, 0.5)');
-def_named_color('MidnightBlue', '(0.0, 0.439988, 0.569994)');
-def_named_color('Mulberry', '(0.640018, 0.0800061, 0.980006)');
-def_named_color('NavyBlue', '(0.0600122, 0.460012, 1.0)');
-def_named_color('OliveGreen', '(0.0, 0.6, 0.0)');
-def_named_color('Orange', '(1.0, 0.389988, 0.130006)');
-def_named_color('OrangeRed', '(1.0, 0.0, 0.5)');
-def_named_color('Orchid', '(0.680006, 0.360012, 1.0)');
-def_named_color('Peach', '(1.0, 0.5, 0.3)');
-def_named_color('Periwinkle', '(0.430006, 0.45, 1.0)');
-def_named_color('PineGreen', '(0.0, 0.75, 0.160012)');
-def_named_color('Plum', '(0.5, 0.0, 1.0)');
-def_named_color('ProcessBlue', '(0.0399878, 1.0, 1.0)');
-def_named_color('Purple', '(0.55, 0.139988, 1.0)');
-def_named_color('RawSienna', '(0.55, 0.0, 0.0)');
-def_named_color('Red', '(1.0, 0.0, 0.0)');
-def_named_color('RedOrange', '(1.0, 0.230006, 0.130006)');
-def_named_color('RedViolet', '(0.590018, 0.0, 0.660012)');
-def_named_color('Rhodamine', '(1.0, 0.180006, 1.0)');
-def_named_color('RoyalBlue', '(0.0, 0.5, 1.0)');
-def_named_color('RoyalPurple', '(0.25, 0.1, 1.0)');
-def_named_color('RubineRed', '(1.0, 0.0, 0.869994)');
-def_named_color('Salmon', '(1.0, 0.469994, 0.619994)');
-def_named_color('SeaGreen', '(0.310012, 1.0, 0.5)');
-def_named_color('Sepia', '(0.3, 0.0, 0.0)');
-def_named_color('SkyBlue', '(0.380006, 1.0, 0.880006)');
-def_named_color('SpringGreen', '(0.739988, 1.0, 0.239988)');
-def_named_color('Tan', '(0.860012, 0.580006, 0.439988)');
-def_named_color('TealBlue', '(0.119994, 0.980006, 0.640018)');
-def_named_color('Thistle', '(0.880006, 0.410012, 1.0)');
-def_named_color('Turquoise', '(0.15, 1.0, 0.8)');
-def_named_color('Violet', '(0.210012, 0.119994, 1.0)');
-def_named_color('VioletRed', '(1.0, 0.189988, 1.0)');
-def_named_color('White', '(1.0, 1.0, 1.0)');
-def_named_color('WildStrawberry', '(1.0, 0.0399878, 0.610012)');
-def_named_color('Yellow', '(1.0, 1.0, 0.0)');
-def_named_color('YellowGreen', '(0.560012, 1.0, 0.260012)');
-def_named_color('YellowOrange', '(1.0, 0.580006, 0.0)');
-
-@ Color commands get a separate warning procedure. |warn| sets |history :=
-warning_given|, which causes a nonzero exit status; but color errors are
-trivial and should leave the exit status zero.
-
-@d color_warn(#)==begin err_print_ln('DVItoMP warning: ',#); if history < warning_given then history := cksum_trouble; end
-
-@ The |do_xxx| procedure handles DVI specials (defined with the
-|xxx1...xxx4| commands).
-
-@<Declare procedures to handle color commands@> =
-procedure do_xxx(p: integer);
-label 9999; {exit procedure}
-const bufsiz = 256; {FIXME: Fixed size buffer.}
-var buf: packed array[0..bufsiz] of eight_bits;
- l, r, m, k, len: integer;
- found: boolean;
-begin
- len := 0;
- while (p > 0) and (len < bufsiz) do begin
- buf[len] := get_byte;
- decr(p); incr(len);
- end;
- @<Check whether |buf| contains a color command; if not, |goto 9999|@>
- if p > 0 then begin
- color_warn('long "color" special ignored'); goto 9999; end;
- if @<|buf| contains a color pop command@> then begin
- @<Handle a color pop command@>
- end else if @<|buf| contains a color push command@> then begin
- @<Handle a color push command@>
- end else begin
- color_warn('unknown "color" special ignored'); goto 9999; end;
-9999: for k := 1 to p do down_the_drain := get_byte;
-end;
-
-@
-
-@<Check whether |buf| contains a color command; if not, |goto 9999|@> =
-if (len <= 5)
- or (buf[0] <> "c")
- or (buf[1] <> "o")
- or (buf[2] <> "l")
- or (buf[3] <> "o")
- or (buf[4] <> "r")
- or (buf[5] <> " ")
- then goto 9999;
-
-@
-
-@<|buf| contains a color push command@> =
-(len >= 11) and (buf[6] = "p") and (buf[7] = "u") and (buf[8] = "s") and (buf[9] = "h") and (buf[10] = " ")
-
-@
-
-@<|buf| contains a color pop command@> =
-(len = 9) and (buf[6] = "p") and (buf[7] = "o") and (buf[8] = "p")
-
-@ The \.{color push} and \.{pop} commands imply a color stack, so we need a
-global variable to hold that stack.
-
-@<Constants...@> =
-max_color_stack_depth=10; {maximum depth of saved color stack}
-
-@ Here's the actual stack variables.
-
-@<Globals...@> =
-color_stack_depth: integer; {current depth of saved color stack}
-color_stack: array[1..max_color_stack_depth] of c_string; {saved color stack}
-
-@ Initialize the stack to empty.
-
-@<Set initial values@> =
-color_stack_depth := 0;
-
-@ \.{color pop} just pops the stack.
-
-@<Handle a color pop command@> =
-finish_last_char;
-if color_stack_depth > 0 then begin
- free(color_stack[color_stack_depth]);
- decr(color_stack_depth)
-end
-else begin
- color_warn('color stack underflow')
-end;
-
-@ \.{color push} pushes a color onto the stack.
-
-@<Handle a color push command@> =
-finish_last_char;
-if color_stack_depth >= max_color_stack_depth then
- abort('color stack overflow');
-incr(color_stack_depth);
-{ I don't know how to do string operations in Pascal. }
-{ Skip over extra spaces after 'color push'. }
-l := 11;
-while (l < len - 1) and (buf[l] = " ") do incr(l);
-if @<|buf[l]| contains an rgb command@> then begin
- @<Handle a color push rgb command@>
-end else if @<|buf[l]| contains a cmyk command@> then begin
- @<Handle a color push cmyk command@>
-end else if @<|buf[l]| contains a gray command@> then begin
- @<Handle a color push gray command@>
-end else begin
- @<Handle a named color push command@>
-end;
-
-@
-
-@<|buf[l]| contains an rgb command@> =
-(l + 4 < len)
-and (buf[l] = "r")
-and (buf[l+1] = "g")
-and (buf[l+2] = "b")
-and (buf[l+3] = " ")
-
-@
-
-@<Handle a color push rgb command@> =
-l := l + 4;
-while (l < len) and (buf[l] = " ") do incr(l);
-{ Remove spaces at end of buf }
-while (len > l) and (buf[len - 1] = " ") do decr(len);
-color_stack[color_stack_depth]:=xmalloc_array(char,len-l+3);
-k := 0;
-@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
-
-@
-
-@<|buf[l]| contains a gray command@> =
-(l + 5 < len)
-and (buf[l] = "g")
-and (buf[l+1] = "r")
-and (buf[l+2] = "a")
-and (buf[l+3] = "y")
-and (buf[l+4] = " ")
-
-@
-
-@<Handle a color push gray command@> =
-l := l + 5;
-while (l < len) and (buf[l] = " ") do incr(l);
-{ Remove spaces at end of buf }
-while (len > l) and (buf[len - 1] = " ") do decr(len);
-color_stack[color_stack_depth]:=xmalloc_array(char,len-l+9);
-strcpy(color_stack[color_stack_depth],'white*');
-k := 6;
-@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
-
-@
-
-@<|buf[l]| contains a cmyk command@> =
-(l + 5 < len)
-and (buf[l] = "c")
-and (buf[l+1] = "m")
-and (buf[l+2] = "y")
-and (buf[l+3] = "k")
-and (buf[l+4] = " ")
-
-@
-
-@<Handle a color push cmyk command@> =
-l := l + 5;
-while (l < len) and (buf[l] = " ") do incr(l);
-{ Remove spaces at end of buf }
-while (len > l) and (buf[len - 1] = " ") do decr(len);
-color_stack[color_stack_depth]:=xmalloc_array(char,len-l+7);
-strcpy(color_stack[color_stack_depth],'cmyk');
-k := 4;
-@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@>
-
-@
-
-@<Copy |buf[l]| to |color_stack[color_stack_depth][k]| in tuple form@> =
-color_stack[color_stack_depth][k] := "(";
-incr(k);
-while l < len do
- if buf[l] = " " then begin
- color_stack[color_stack_depth][k] := ",";
- while (l < len) and (buf[l] = " ") do incr(l);
- incr(k);
- end else begin
- color_stack[color_stack_depth][k] := buf[l];
- incr(l);
- incr(k);
- end;
-color_stack[color_stack_depth][k] := ")";
-color_stack[color_stack_depth][k+1] := chr(0)
-
-@ Binary-search the |named_colors| array, then push the found color onto
-the stack.
-
-@<Handle a named color push command@> =
-for k := l to len - 1 do begin
- buf[k - l] := xchr[buf[k]];
-end;
-buf[len - l] := 0;
-len := len - l;
-l := 1; r := num_named_colors;
-found := false;
-while (l <= r) and not found do begin
- m := (l + r) / 2;
- k := strcmp(stringcast(buf), named_colors[m].name);
- if k = 0 then begin
- color_stack[color_stack_depth]:=xstrdup(named_colors[m].value);
- found := true;
- end else if k < 0 then
- r := m - 1
- else
- l := m + 1;
-end;
-if not found then begin
- color_warn('non-hardcoded color "',stringcast(buf),'" in "color push" command');
- color_stack[color_stack_depth]:=xstrdup(stringcast(buf));
-end;
-
-@ Last but not least, this code snippet prints a \.{withcolor} specifier
-for the top of the color stack, if the stack is nonempty.
-
-@<Print a \.{withcolor} specifier if appropriate@> =
-if color_stack_depth > 0 then begin
- print(' withcolor ');
- fputs(color_stack[color_stack_depth], mpx_file);
-end;
-
-@z
diff --git a/Build/source/texk/web2c/dvitomp.mk b/Build/source/texk/web2c/dvitomp.mk
deleted file mode 100644
index 9a7efd5f0a1..00000000000
--- a/Build/source/texk/web2c/dvitomp.mk
+++ /dev/null
@@ -1,27 +0,0 @@
-# Makefile fragment for DVItoMP.
-# $Id: dvitomp.mk,v 1.1 2005/06/21 16:03:15 olaf Exp $
-#
-# Public domain.
-#
-# This facilitates a separate development track for DVItoMP.
-
-Makefile: dvitomp.mk
-
-dvitomp: dvitomp.o
- $(kpathsea_link) dvitomp.o $(LOADLIBES)
-dvitomp.c dvitomp.h: $(web2c_common) $(web2c_programs) dvitomp.p
- $(web2c) dvitomp
-dvitomp.p: tangle dvitomp.web dvitomp.ch
- $(tangle) dvitomp dvitomp
-check: dvitomp-check
-dvitomp-check: dvitomp
- TEXMFCNF=../kpathsea \
- ./dvitomp $(srcdir)/tests/story.dvi tests/xstory.mpx
- TFMFONTS=$(srcdir)/tests VFFONTS=$(srcdir)/tests: \
- ./dvitomp $(srcdir)/tests/ptmr
- mv ptmr.mpx tests/xptmr.mpx
-clean:: dvitomp-clean
-dvitomp-clean:
- $(LIBTOOL) --mode=clean rm -f dvitomp
- rm -f dvitomp.o dvitomp.c dvitomp.h dvitomp.p
- rm -f tests/xstory.mpx tests/xptmr.mpx
diff --git a/Build/source/texk/web2c/dvitomp.web b/Build/source/texk/web2c/dvitomp.web
deleted file mode 100644
index da6ce104e44..00000000000
--- a/Build/source/texk/web2c/dvitomp.web
+++ /dev/null
@@ -1,1680 +0,0 @@
-% $Id: dvitomp.web,v 1.3 2005/04/28 06:45:21 taco Exp $
-% DVItoMP, by John Hobby. Public domain.
-
-% Version 0 was prepared (March 1990).
-% Version 0.1 implemented virtual fonts. (May 1990)
-% Version 0.30 outputs setbounds statments, ignores rules 1sp wide (May 1991)
-% Version 0.60 outputs rules as penstrokes with butt ends
-% Version 0.62 makes the output more robust when used in a macro definition
-% Version 0.63 new version number only (Unix change file has // path searching)
-% Version 0.632 outputs a setbounds path even if no 1sp vrule appears (Jan 1997)
-% Version 0.64 avoids outputting wrong fonts, improves error handling (Jan 1998)
-
-% Although considerable effort has been expended to make the DVItoMP 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 program is loosely based on DVItype Version 3.0
-% It converts a DVI file into a sequence of MetaPost picture expressions.
-
-% TeX is a trademark of the American Mathematical Society.
-
-% Here is TeX material that gets inserted after \input webmac
-\def\hang{\hangindent 3em\indent\ignorespaces}
-\font\ninerm=cmr9
-\font\sc=cmcsc10
-\let\mc=\ninerm % medium caps for names like SAIL
-\def\MP{MetaPost}
-\def\LaTeX{{\rm L\kern-.36em\raise.3ex\hbox{\sc a}\kern-.15em
- T\kern-.1667em\lower.7ex\hbox{E}\kern-.125emX}}
-\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{to}MP}
-\def\contentspagenumber{1}
-\def\topofcontents{\null
- \def\titlepage{F} % include headline on the contents page
- \def\rheader{\mainfont\hfil \contentspagenumber}
- \vfill
- \centerline{\titlefont The {\ttitlefont DVItoMP} processor}
- \vskip 15pt
- \centerline{(Version 0.64, January 1998)}
- \vfill}
-\pageno=\contentspagenumber \advance\pageno by 1
-
-@* Introduction.
-The \.{DVItoMP} program reads binary device-independent (``\.{DVI}'')
-files that are produced by document compilers such as \TeX, and converts them
-into a symbolic form understood by \MP. It is loosely based on the \.{DVItype}
-utility program that produces a more faithful symbolic form of a \.{DVI} file.
-
-The output file is a sequence of \MP\ picture expressions, one for every page
-in the \.{DVI} file. It makes no difference to \.{DVItoMP} where the \.{DVI}
-file comes from, but it is intended to process the result of running \TeX\
-or \LaTeX\ on the output of \.{MPtoTEX}. Such a \.{DVI} file will contain
-one page for every \.{btex}$\ldots$\.{etex} block in the original input.
-Processing in with \.{DVItoMP} creates a corresponding sequence of \MP\ picture
-expressions for use as an auxiliary input file. Since \MP\ expects such files
-to have the extension \.{.MPX}, the output of \.{DVItoMP} is sometimes called
-an ``\.{MPX}'' file.
-
-The |banner| string defined here should be changed whenever \.{DVItoMP}
-gets modified.
-
-@d banner=='% Written by DVItoMP, Version 1.003'
- {the first line of the output file}
-
-@ This program is written in standard \PASCAL, except where it is necessary
-to use extensions; for example, \.{DVItoMP} 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@>
-
-Many \.{DVI}-reading programs need the ability to move to a random place
-in a binary file. \.{DVItoMP} does not need to do this, but it does use
-a default |case| as in \.{TANGLE}, \.{WEAVE}, etc.
-
-@ The binary input comes from |dvi_file|, and the symbolic output goes to
-|mpx_file|. \PASCAL's standard |output| file is used only to print an error
-message if the \.{DVI} file is bad. The term |print| is used instead of
-|write| when this program writes on |mpx_file| just in case this helps
-some installations deal with system dependencies.
-@^system dependencies@>
-
-@d print(#)==write(mpx_file,#)
-@d print_ln(#)==write_ln(mpx_file,#)
-@d err_print(#)==write(#)
-@d err_print_ln(#)==write_ln(#)
-
-@p program DVI_to_MP(@!dvi_file,@!mpx_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 @<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 \.{DVItoMP}'s capacity.
-
-@<Constants...@>=
-@!max_fonts=1000; {maximum number of distinct fonts per \.{DVI} file}
-@!max_fnums=3000; {maximum number of fonts plus fonts local to virtual fonts}
-@!max_widths=256*max_fonts; {maximum number of different characters among all fonts}
-@!virtual_space=1000000;
- {maximum total bytes of typesetting commands for virtual fonts}
-@!line_length=79; {maximum output line length (must be at least 60)}
-@!stack_size=100; {\.{DVI} files shouldn't |push| beyond this depth}
-@!name_size=100000; {total length of all font file names}
-@!name_length=250; {a file name shouldn't be longer than this}
-
-@ There is one more parameter that is a little harder to change because
-it is of type |real|.
-
-@d font_tolerance==0.00001
- {font sizes should match to within this multiple of $2^{20}$ \.{DVI} units}
-
-@ 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}
-
-@ A global variable |history| keeps track of what type of errors have
-occurred with the hope that that \MP\ can be warned of any problems.
-
-@d spotless=0 {|history| value when no problems have been found}
-@d cksum_trouble=1 {|history| value there have been font checksum mismatches}
-@d warning_given=2 {|history| value after a recoverable error}
-@d fatal_error=3 {|history| value if processing had to be aborted}
-
-@<Glob...@>=
-history:spotless..fatal_error;
-
-@ @<Set init...@>=
-history:=spotless;
-
-@ If the \.{DVI} file is badly malformed, the whole process must be aborted;
-\.{DVItoMP} 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 \.{DVItoMP}.
-@^system dependencies@>
-
-@d abort(#)==begin err_print_ln('DVItoMP abort: ',#);
- history:=fatal_error; jump_out;
- end
-@d bad_dvi(#)==abort('Bad DVI file: ',#,'!')
-@.Bad DVI file@>
-@d warn(#)==begin err_print_ln('DVItoMP warning: ',#);
- history:=warning_given;
- end
-
-@p procedure jump_out;
-begin goto final_end;
-end;
-
-@* The character set.
-Like all programs written with the \.{WEB} system, \.{DVItoMP} 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.
-
-The next few sections of \.{DVItoMP} have therefore been copied from the
-analogous ones in the \.{WEB} system routines. They have been considerably
-simplified, since \.{DVItoMP} need not deal with the controversial
-ASCII codes less than @'40 or greater than @'176.
-If such codes appear in the font names,
-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 \.{DVItoMP}.
-So we shall assume that the \PASCAL\ system being used for \.{DVItoMP}
-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 \.{DVItoMP} processor converts from ASCII code to
-the user's external character set by means of an array |xchr|
-that is analogous to \PASCAL's |chr| function.
-
-@<Globals...@>=
-@!mpx_file:text_file; {destination for printed output}
-@!xchr: array [0..255] of text_char;
- {specifies conversion of output characters}
-
-@ To prepare the |mpx_file| for output, we |rewrite| it.
-@^system dependencies@>
-
-@p procedure open_mpx_file; {prepares to write text on |mpx_file|}
-begin rewrite(mpx_file);
-end;
-
-@ 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]:='?';
-
-@* Device-independent file format.
-The format of \.{DVI} files is described in many places including
-\.{dvitype.web} and Volume~B of D.~E. Knuth's {\sl Computers and Typesetting}.
-This program refers to the following command codes.
-
-@d id_byte=2 {identifies the kind of \.{DVI} files described here}
-@#
-@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
-
-@* 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
-\.{DVItoMP} 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. It is convenient to have a throw-away variable for function
-results when reading parts of the files that are being skipped.
-
-@<Glob...@>=
-@!dvi_file:byte_file; {the input file}
-@!tfm_file:byte_file; {a font metric file}
-@!vf_file:byte_file; {a virtual font file}
-@!down_the_drain:integer; {a ``write-only'' variable}
-
-@ 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, these routines 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);
-if eof(dvi_file) then abort('DVI file not found');
-end;
-@#
-function open_tfm_file:boolean; {prepares to read packed bytes in |tfm_file|}
-begin reset(tfm_file,cur_name);
-open_tfm_file:=(not eof(tfm_file));
-end;
-@#
-function open_vf_file:boolean; {prepares to read packed bytes in |vf_file|}
-begin reset(vf_file,cur_name);
-open_vf_file:=(not eof(vf_file));
-end;
-
-@ If you looked carefully at the preceding code, you probably asked,
-``What is |cur_name|?'' Good question. It's a global
-variable: |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_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;
-
-@ Input can come from from three different sources depending on the settings
-of global variables. When |vf_reading| is true, we read from the \.{VF} file.
-Otherwise, input can either come directly from |dvi_file| or from a buffer
-|cmd_buf|. The latter case applies whenever |buf_ptr<virtual_space|.
-
-@<Glob...@>=
-@!vf_reading:boolean; {should input come from |vf_file|?}
-@!cmd_buf:packed array [0..virtual_space] of quarter_word;
- {commands for virtual characters}
-@!buf_ptr:0..virtual_space; {|cmd_buf| index for the next byte}
-
-@ @<Set init...@>=
-vf_reading:=false; buf_ptr:=virtual_space;
-
-@ It is probably not critical that |cmd_buf| be packed as efficiently as possible,
-but we define a new type just in case it is necessary to for |cmd_buf| entries
-to be in the range |-128..127|.
-@^system dependencies@>
-
-@d qi(#)==# {convert from |eight_bits| to |quarter_word|}
-@d qo(#)==# {convert from |quarter_word| to |eight_bits|}
-
-@<Types...@>=
-quarter_word=0..255; {a one byte quantity as stored in |cmd_buf|}
-
-@ We shall use a set of simple functions to read the next byte or bytes from the
-current input source. There are seven possibilities, each of which is treated
-as a separate function in order to minimize the overhead for subroutine calls.
-
-@p function get_byte:integer; {returns the next byte, unsigned}
-var b:eight_bits;
-begin @<Read one byte into |b|@>;
-get_byte:=b;
-end;
-@#
-function signed_byte:integer; {returns the next byte, signed}
-var b:eight_bits;
-begin @<Read one byte into |b|@>;
-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 a:=0;b:=0; {for compiler warnings}
-@<Read two bytes into |a| and |b|@>;
-get_two_bytes:=a*256+b;
-end;
-@#
-function signed_pair:integer; {returns the next two bytes, signed}
-var a,@!b:eight_bits;
-begin a:=0;b:=0; {for compiler warnings}
-@<Read two bytes into |a| and |b|@>;
-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 a:=0;b:=0;c:=0; {for compiler warnings}
-@<Read three bytes into |a|, |b|, and~|c|@>;
-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 a:=0;b:=0;c:=0; {for compiler warnings}
-@<Read three bytes into |a|, |b|, and~|c|@>;
-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 a:=0;b:=0;c:=0;d:=0; {for compiler warnings}
-@<Read four bytes into |a|, |b|, |c|, and~|d|@>;
-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;
-
-@ @<Read one byte into |b|@>=
-if vf_reading then read(vf_file,b)
-else if buf_ptr=virtual_space then read(dvi_file,b)
-else begin b:=qo(cmd_buf[buf_ptr]);
- incr(buf_ptr);
- end
-
-@ @<Read two bytes into |a| and |b|@>=
-if vf_reading then
- begin read(vf_file,a); read(vf_file,b);
- end
-else if buf_ptr=virtual_space then
- begin read(dvi_file,a); read(dvi_file,b);
- end
-else if buf_ptr+2>n_cmds then
- abort('Error detected while interpreting a virtual font')
-@.Error detected while...@>
-else begin a:=qo(cmd_buf[buf_ptr]);
- b:=qo(cmd_buf[buf_ptr+1]);
- buf_ptr:=buf_ptr+2;
- end
-
-@ @<Read three bytes into |a|, |b|, and~|c|@>=
-if vf_reading then
- begin read(vf_file,a); read(vf_file,b); read(vf_file,c);
- end
-else if buf_ptr=virtual_space then
- begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c);
- end
-else if buf_ptr+3>n_cmds then
- abort('Error detected while interpreting a virtual font')
-@.Error detected while...@>
-else begin a:=qo(cmd_buf[buf_ptr]);
- b:=qo(cmd_buf[buf_ptr+1]);
- c:=qo(cmd_buf[buf_ptr+2]);
- buf_ptr:=buf_ptr+3;
- end
-
-@ @<Read four bytes into |a|, |b|, |c|, and~|d|@>=
-if vf_reading then
- begin read(vf_file,a); read(vf_file,b); read(vf_file,c); read(vf_file,d);
- end
-else if buf_ptr=virtual_space then
- begin read(dvi_file,a); read(dvi_file,b); read(dvi_file,c); read(dvi_file,d);
- end
-else if buf_ptr+4>n_cmds then
- abort('Error detected while interpreting a virtual font')
-@.Error detected while...@>
-else begin a:=qo(cmd_buf[buf_ptr]);
- b:=qo(cmd_buf[buf_ptr+1]);
- c:=qo(cmd_buf[buf_ptr+2]);
- d:=qo(cmd_buf[buf_ptr+3]);
- buf_ptr:=buf_ptr+4;
- end
-
-@* Data structures for fonts.
-\.{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 \.{DVItoMP} looks at the font metric
-(\.{TFM}) files for the fonts that are involved.
-@.TFM {\rm files}@>
-
-@ For purposes of this program, the only thing we need to know about a
-given character |c| in a non-virtual font |f| is the width. For the font as
-a whole, all we need is the symbolic name to use in the \.{MPX} file.
-
-This information appears implicitly in the following data
-structures. The current number of fonts defined is |nf|. Each such font has
-an internal number |f|, where |0<=f<nf|. There is also an external number
-that identifies the font in the \.{DVI} file. The correspondence is
-maintained in arrays |font_num| and |internal_num| so that |font_num[i]|
-is the external number for |f=internal_num[i]|.
-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. The
-legal characters run from |font_bc[f]| to |font_ec[f]|, inclusive.
-The \.{TFM} file can specify that some of these are invalid, but this doesn't
-concern \.{DVItoMP} because it does not do extensive error checking.
-The width of character~|c| in font~|f| is given by
-|char_width(f)(c)=width[info_base[f]+c]|, and |info_ptr| is the
-first unused position of the |width| array.
-
-If font~|f| is a virtual font, there is a list of \.{DVI} commands for each
-character. These occupy consecutive positions in the |cmd_buf| array with
-the commands for character~|c| starting at
-|start_cmd(f)(c)=cmd_ptr[info_base[f]+c]| and ending just before
-|start_cmd(f)(c+1)|. Font numbers used when interpreting these \.{DVI}
-commands occupy positions |fbase[f]| through |ftop[f]-1| in the |font_num|
-table and the |internal_num| array gives the corresponding internal font
-numbers. If such an internal font number~|i| does not correspond to
-some font occuring in the \.{DVI} file, then |font_num[i]| has not been
-assigned a meaningful value; this is indicated by |local_only[i]=true|.
-
-If font~|f| is not virtual, then |fbase[f]=0| and |ftop[f]=0|. The |start_cmd|
-values are ignored in this case.
-
-@d char_width_end(#)==#]
-@d char_width(#)==width[info_base[#]+char_width_end
-@d start_cmd(#)==cmd_ptr[info_base[#]+start_cmd_end
-@d start_cmd_end(#)==#]
-
-@<Glob...@>=
-@!font_num:array [0..max_fnums] of integer; {external font numbers}
-@!internal_num:array [0..max_fnums] of integer; {internal font numbers}
-@!local_only:array [0..max_fonts] of boolean; {|font_num| meaningless?}
-@!font_name:array [0..max_fonts] of 0..name_size; {starting positions
- of external font names}
-@!names:array [0..name_size] of ASCII_code; {characters of names}
-@!area_length:array [0..max_fonts] of integer;
- {length of area part of font name}
-@!font_scaled_size:array [0..max_fonts] of real; {scale factors over $2^{20}$}
-@!font_design_size:array [0..max_fonts] of real; {design sizes over $2^{20}$}
-@!font_check_sum:array [0..max_fonts] of integer;
- {check sum from the |font_def|}
-@!font_bc:array [0..max_fonts] of integer; {beginning characters in fonts}
-@!font_ec:array [0..max_fonts] of integer; {ending characters in fonts}
-@!info_base:array [0..max_fonts] of integer;
- {index into |width| and |cmd_ptr| tables}
-@!width:array [0..max_widths] of integer;
- {character widths, in units $2^{-20}$ of design size}
-@!fbase:array [0..max_fonts] of integer; {index into |font_num| for local fonts}
-@!ftop:array [0..max_fonts] of integer;
- {|font_num| index where local fonts stop}
-@!cmd_ptr:array [0..max_widths] of integer; {starting positions in |cmd_buf|}
-@!nf:0..max_fonts; {the number of known fonts}
-@!vf_ptr:max_fonts..max_fnums;
- {next |font_num| entry for virtual font font tables}
-@!info_ptr:0..max_widths; {allocation pointer for |width| and |cmd_ptr| tables}
-@!n_cmds:0..virtual_space; {number of occupied cells in |cmd_buf|}
-@!cur_fbase,@!cur_ftop:0..max_fnums;
- {currently applicable part of the |font_num| table}
-
-@ @<Set init...@>=
-nf:=0; info_ptr:=0; font_name[0]:=0;
-vf_ptr:=max_fnums;
-cur_fbase:=0; cur_ftop:=0;
-
-@ Printing the name of a given font is easy except that a procedure |print_char|
-is needed to actually send an |ASCII_code| to the \.{MPX} file.
-
-@p @<Declare subroutines for printing strings@>@;
-procedure print_font(@!f:integer); {|f| is an internal font number}
-var k:0..name_size; {index into |names|}
-begin if (f<0)or(f>=nf) then bad_dvi('Undefined font')
-else begin for k:=font_name[f] to font_name[f+1]-1 do
- print_char(names[k]);
- end;
-end;
-
-@ Sometimes a font name is needed as part of an error message.
-
-@d font_warn(#)==begin err_print('DVItoMP warning: ',#); font_warn_end
-@d font_warn_end(#)==err_print_font(#);
- history:=warning_given;
- end
-@d font_abort(#)==begin err_print('DVItoMP abort: ',#); font_abort_end
-@d font_abort_end(#)==err_print_font(#);
- history:=fatal_error; jump_out;
- end
-
-@p procedure err_print_font(@!f:integer); {|f| is an internal font number}
-var k:0..name_size; {index into |names|}
-begin for k:=font_name[f] to font_name[f+1]-1 do
- err_print(xchr[names[k]]);
- err_print_ln(' ');
-end;
-
-@ When we encounter a font definition, we save the name, checksum, and size
-information, but we don't actually read the \.{TFM} or \.{VF} file until we
-are about to use the font. If a matching font is not already defined, we then
-allocate a new internal font number.
-
-The following subroutine does the necessary things when a \\{fnt\_def} command
-is encountered in the \.{DVI} file or in a \.{VF} file. It assumes that the
-first argument has already been parsed and is given by the parameter~|e|.
-
-@p @<Declare a function called |match_font|@>@;
-procedure define_font(@!e:integer); {|e| is an external font number}
-var @!i:integer; {index into |font_num| and |internal_num|}
-@!n:integer; {length of the font name and area}
-@!k:integer; {general purpose loop counter}
-@!x:integer; {a temporary value for scaled size computation}
-begin if nf=max_fonts then abort('DVItoMP capacity exceeded (max fonts=',
- max_fonts:1,')!');
-@.DVItoMP capacity exceeded...@>
-@<Allocate an index |i| into the |font_num| and |internal_num| tables@>;
-@<Read the font parameters into position for font |nf|@>;
-internal_num[i]:=match_font(nf,true);
-if internal_num[i]=nf then
- begin info_base[nf]:=max_widths; {indicate that the info isn't loaded yet}
- local_only[nf]:=vf_reading; incr(nf);
- end;
-end;
-
-@ @<Allocate an index |i| into the |font_num| and |internal_num| tables@>=
-if vf_ptr=nf then abort('DVItoMP capacity exceeded (max font numbers=',
- max_fnums:1,')');
-@.DVItoMP capacity exceeded...@>
-if vf_reading then
- begin font_num[nf]:=0; i:=vf_ptr; decr(vf_ptr);
- end
-else i:=nf;
-font_num[i]:=e
-
-@ @<Read the font parameters into position for font |nf|@>=
-font_check_sum[nf]:=signed_quad;
-@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>;
-n:=get_byte; area_length[nf]:=n;
-n:=n+get_byte;
-if font_name[nf]+n>name_size then
- abort('DVItoMP capacity exceeded (name size=',name_size:1,')!');
-@.DVItoMP capacity exceeded...@>
-font_name[nf+1]:=font_name[nf]+n;
-for k:=font_name[nf] to font_name[nf+1]-1 do names[k]:=get_byte
-
-@ The scaled size and design size are stored in \.{DVI} units divided by $2^{20}$.
-The units for scaled size are a little different if we are reading a virtual
-font, but this will be corrected when the scaled size is used. The scaled size
-also needs to be truncated to at most 23 significant bits in order to make
-the character width calculation match what \TeX\ does.
-
-@<Read |font_scaled_size[nf]| and |font_design_size[nf]|@>=
-x:=signed_quad;
-k:=1;
-while x>@'40000000 do
- begin x:=x div 2; k:=k+k;
- end;
-font_scaled_size[nf]:=x*k/1048576.0;
-if vf_reading then
- font_design_size[nf]:=signed_quad*dvi_per_fix/1048576.0
-else font_design_size[nf]:=signed_quad/1048576.0;
-
-@ @<Glob...@>=
-@!dvi_per_fix:real; {converts points scaled $2^{20}$ to \.{DVI} units}
-
-@ The |match_font| function tries to find a match for the font with internal
-number~|ff|, returning |nf| or the number of the matching font. If
-|exact=true|, the name and scaled size should match. Otherwise the scaled
-size need not match but the font found must be already loaded, not just
-defined.
-
-@<Declare a function called |match_font|@>=
-function match_font(ff:integer; exact:boolean):integer;
-label done, 99;
-var @!f:0..max_fonts; {font number being tested}
-@!ss,@!ll:0..name_size; {starting point and length of name of font |ff|}
-@!k,@!s:0..name_size; {registers for comparing font names}
-begin ss:=font_name[ff]; ll:=font_name[ff+1]-ss;
-f:=0;
-while f<nf do
- begin if f<>ff then
- begin @<Compare the names of fonts |f| and |ff|; |goto 99| if they differ@>;
- if exact then
- begin if abs(font_scaled_size[f]-font_scaled_size[ff])
- <= font_tolerance then
- begin if not vf_reading then begin
- if local_only[f] then
- begin font_num[f]:=font_num[ff]; local_only[f]:=false;
- end
- else if font_num[f]<>font_num[ff] then goto 99;
- end;
- goto done;
- end;
- end
- else if info_base[f]<>max_widths then goto done;
- end;
- 99:incr(f);
- end;
-done:if f<nf then begin
- @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>;
- end;
-match_font:=f;
-end;
-
-@ @<Compare the names of fonts |f| and |ff|; |goto 99| if they differ@>=
-if (area_length[f]<area_length[ff]) or (ll<>font_name[f+1]-font_name[f]) then
- goto 99;
-s:=font_name[f];
-k:=ll;
-while k>0 do
- begin decr(k);
- if names[s+k]<>names[ss+k] then goto 99;
- end
-
-@ @<Make sure fonts |f| and |ff| have matching design sizes and checksums@>=
-if abs(font_design_size[f]-font_design_size[ff]) > font_tolerance then
- font_warn('Inconsistent design sizes given for ')(ff)
-@.Inconsistent design sizes@>
-else if font_check_sum[f]<>font_check_sum[ff] then
- font_warn('Checksum mismatch for ')(ff)
-@.Checksum mismatch@>
-
-@* Reading ordinary fonts.
-An auxiliary array |in_width| is used to hold the widths as they are
-input. The global variable |tfm_check_sum| is set to the check sum that
-appears 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|}
-
-@ 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. The procedure simply
-aborts the program if it detects anything amiss in the \.{TFM} data.
-
-@p procedure in_TFM(@!f:integer);
- {input \.{TFM} data for font |f| or abort}
-label 9997, {go here when the format is bad}
- 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 |info_ptr| after successful input}
-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 the width values into the |in_width| table@>;
-@<Move the widths from |in_width| to |width|@>;
-fbase[f]:=0; ftop[f]:=0;
-info_ptr:=wp; goto 9999;
-9997: font_abort('Bad TFM file for ')(f);
-@.Bad TFM file@>
-9999: end;
-
-@ @<Read past the header...@>=
-read_tfm_word; lh:=b2*256+b3;
-read_tfm_word; font_bc[f]:=b0*256+b1; font_ec[f]:=b2*256+b3;
-if font_ec[f]<font_bc[f] then font_bc[f]:=font_ec[f]+1;
-if info_ptr+font_ec[f]-font_bc[f]+1>max_widths then
- abort('DVItoMP capacity exceeded (width table size=',max_widths:1,')!');
-@.DVItoMP capacity exceeded...@>
-wp:=info_ptr+font_ec[f]-font_bc[f]+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 begin
- 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;
- end;
- end;
-
-@ @<Store character-width indices...@>=
-if wp>0 then for k:=info_ptr to wp-1 do
- begin read_tfm_word;
- if b0>nw then goto 9997;
- width[k]:=b0;
- end;
-
-@ No fancy width calculation is needed here because \.{DVItoMP} stores
-widths in their raw form as multiples of the design size scaled by $2^{20}$.
-The |font_scaled_size| entries have been computed so that the final width
-compution can be done in floating point if enough precision is available.
-
-@<Read the width values into the |in_width| table@>=
-for k:=0 to nw-1 do
- begin read_tfm_word;
- if b0>127 then b0:=b0-256;
- in_width[k]:=((b0*@'400+b1)*@'400+b2)*@'400+b3;
- end
-
-@ The width compution uses a scale factor |dvi_scale| that will be introduced
-later. It is equal to one when not typesetting a character from a virtual
-font. In that case, the following expressions do the width computation that is
-so important in \.{DVItype}. It is less important here because it is impractical
-to guarantee precise character positioning in \MP\ output. Nevertheless, the
-width compution will be precise if reals have at least 46-bit mantissas and
-|round(x-.5)| is equivalent to $\lfloor x\rfloor$. It may be a good idea to
-modify this computation if these conditions are not met.
-@^system dependencies@>
-
-@<Width of character |c| in font |f|@>=
-round(dvi_scale*font_scaled_size[f]*char_width(f)(c)-0.5)
-
-@ @<Width of character |p| in font |cur_font|@>=
-round(dvi_scale*font_scaled_size[cur_font]*char_width(cur_font)(p)-0.5)
-
-@ @<Move the widths from |in_width| to |width|@>=
-if in_width[0]<>0 then goto 9997; {the first width should be zero}
-info_base[f]:=info_ptr-font_bc[f];
-if wp>0 then for k:=info_ptr to wp-1 do
- width[k]:=in_width[width[k]]
-
-
-@* Reading virtual fonts.
-The |in_VF| procedure absorbs the necessary information from a \.{VF} file that
-has just been reset so that we are ready to read the first byte. (A complete
-description of \.{VF} file format appears in the documention of \.{VFtoVP}).
-Like |in_TFM|, this procedure simply aborts the program if it detects anything
-wrong with the \.{VF} file.
-
-@p @<Declare a function called |first_par|@>@;
-procedure in_VF(f:integer);
- {read \.{VF} data for font |f| or abort}
-label 9997, {go here to abort}
- 9999; {go here to exit}
-var @!p:integer; {a byte from the \.{VF} file}
-@!was_vf_reading:boolean; {old value of |vf_reading|}
-@!c:integer; {the current character code}
-@!limit:integer; {space limitations force character codes to be less than this}
-@!w:integer; {a \.{TFM} width being read}
-begin was_vf_reading:=vf_reading; vf_reading:=true;
-@<Start reading the preamble from a \.{VF} file@>;@/
-@<Initialize the data structures for the virtual font@>;@/
-p:=get_byte;
-while p>=fnt_def1 do
- begin if p>fnt_def1+3 then goto 9997;
- define_font(first_par(p));
- p:=get_byte;
- end;
-while p<=242 do
- begin if eof(vf_file) then goto 9997;
- @<Read the packet length, character code, and \.{TFM} width@>;
- @<Store the character packet in |cmd_buf|@>;
- p:=get_byte;
- end;
-if p=post then
- begin @<Finish setting up the data structures for the new virtual font@>;
- goto 9999;
- end;
-9997:font_abort('Bad VF file for ')(f);
-9999: vf_reading:=was_vf_reading;
-end;
-
-@ @<Start reading the preamble from a \.{VF} file@>=
-p:=get_byte;
-if p<>pre then goto 9997;
-p:=get_byte; {fetch the identification byte}
-if p<>202 then goto 9997;
-p:=get_byte; {fetch the length of the introductory comment}
-while p>0 do
- begin decr(p); down_the_drain:=get_byte;
- end;
-tfm_check_sum:=signed_quad;
-down_the_drain:=signed_quad; {skip over the design size}
-
-@ @<Initialize the data structures for the virtual font@>=
-ftop[f]:=vf_ptr;
-if vf_ptr=nf then abort('DVItoMP capacity exceeded (max font numbers=',
- max_fnums:1,')');
-@.DVItoMP capacity exceeded...@>
-decr(vf_ptr);
-info_base[f]:=info_ptr;
-limit:=max_widths-info_base[f];@/
-font_bc[f]:=limit; font_ec[f]:=0
-
-@ @<Read the packet length, character code, and \.{TFM} width@>=
-if p=242 then
- begin p:=signed_quad; c:=signed_quad; w:=signed_quad;
- if c<0 then goto 9997;
- end
-else begin c:=get_byte; w:=get_three_bytes;
- end;
-if c>=limit then
- abort('DVItoMP capacity exceeded (max widths=', max_widths:1,')!');
-@.DVItoMP capacity exceeded...@>
-if c<font_bc[f] then font_bc[f]:=c;
-if c>font_ec[f] then font_ec[f]:=c;
-char_width(f)(c):=w
-
-@ @<Store the character packet in |cmd_buf|@>=
-if n_cmds+p>=virtual_space then
- abort('DVItoMP capacity exceeded (virtual font space=',virtual_space:1,')!');
-@.DVItoMP capacity exceeded...@>
-start_cmd(f)(c):=n_cmds;
-while p>0 do
- begin cmd_buf[n_cmds]:=qi(get_byte);
- incr(n_cmds); decr(p);
- end;
-cmd_buf[n_cmds]:=qi(eop); {add the end-of-packet marker}
-incr(n_cmds)
-
-@ There are unused |width| and |cmd_ptr| entries if |font_bc[f]>0| but it isn't
-worthwhile to slide everything down just to save a little space.
-
-@<Finish setting up the data structures for the new virtual font@>=
-fbase[f]:=vf_ptr+1;
-info_ptr:=info_base[f]+font_ec[f]+1
-
-
-@* Loading fonts.
-The character width information for a font is loaded when the font is selected
-for the first time. This information might already be loaded if the font has
-already been used at a different scale factor. Otherwise, we look for a \.{VF}
-file, or failing that, a \.{TFM} file. All this is done by the |select_font|
-function that takes an external font number~|e| and returns the corresponding
-internal font number with the width information loaded.
-
-@p function select_font(@!e:integer):integer;
-var @!f:0..max_fonts; {the internal font number}
-@!ff:0..max_fonts; {internal font number for an existing version}
-@!k:integer; {general purpose loop counter}
-begin @<Set |f| to the internal font number that corresponds to |e|,
- or |abort| if there is none@>;
-if info_base[f]=max_widths then
- begin ff:=match_font(f,false);
- if ff<nf then @<Make font |f| refer to the width information from font |ff|@>
- else begin @<Move the \.{VF} file name into the |cur_name| string@>;
- if open_vf_file then in_VF(f)
- else begin @<Change \.{.VF} to \.{.TFM} in the |cur_name| string@>;
- if not open_tfm_file then font_abort('No TFM file found for ')(f);
- @.no TFM file found@>
- in_TFM(f);
- end;
- @<Make sure the checksum in the font file matches the one given in the
- |font_def| for font |f|@>;
- end;
- @<Do any other initialization required for the new font |f|@>;
- end;
-select_font:=f;
-end;
-
-@ @<Set |f| to the internal font number that corresponds to |e|,...@>=
-if cur_ftop<=nf then cur_ftop:=nf;
-font_num[cur_ftop]:=e;
-k:=cur_fbase;
-while (font_num[k]<>e)or local_only[k] do incr(k);
-if k=cur_ftop then abort('Undefined font selected');
-f:=internal_num[k]
-
-@ @<Make font |f| refer to the width information from font |ff|@>=
-begin font_bc[f]:=font_bc[ff];
-font_ec[f]:=font_ec[ff];
-info_base[f]:=info_base[ff];
-fbase[f]:=fbase[ff];
-ftop[f]:=ftop[ff];
-end
-
-@ If |area_length[f]=0|, i.e., if no font directory has been specified,
-\.{DVItoMP} 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
-\.{VF} file for the current font. This usually means that we need to
-prepend the name of the default directory, and
-to append the suffix `\.{.VF}'. Furthermore, we change lower case letters
-to upper case, since |cur_name| is a \PASCAL\ string.
-@^system dependencies@>
-
-@<Move the \.{VF} file name into the |cur_name| string@>=
-for k:=1 to name_length do cur_name[k]:=' ';
-if area_length[f]=0 then
- begin for k:=1 to default_directory_name_length do
- cur_name[k]:=default_directory[k];
- l:=default_directory_name_length;
- end
-else l:=0;
-for k:=font_name[f] to font_name[f+1]-1 do
- begin incr(l);
- if l+3>name_length then
- abort('DVItoMP capacity exceeded (max font name length=',
- name_length:1,')!');
-@.DVItoMP capacity exceeded...@>
- if (names[k]>="a")and(names[k]<="z") then
- cur_name[l]:=xchr[names[k]-@'40]
- else cur_name[l]:=xchr[names[k]];
- end;
-cur_name[l+1]:='.'; cur_name[l+2]:='V'; cur_name[l+3]:='F'
-
-@ It is fairly simple to change a \.{VF} file name to a \.{TFM} file name.
-@^system dependencies@>
-
-@<Change \.{.VF} to \.{.TFM} in the |cur_name| string@>=
-l:=area_length[f];
-if l=0 then l:=default_directory_name_length;
-l:=l+font_name[f+1]-font_name[f];
-if l+4>name_length then
- abort('DVItoMP capacity exceeded (max font name length=',
- name_length:1,')!');
-@.DVItoMP capacity exceeded...@>
-cur_name[l+2]:='T'; cur_name[l+3]:='F'; cur_name[l+4]:='M'
-
-@ @<Make sure the checksum in the font file matches the one given in the...@>=
-begin if (font_check_sum[f]<>0)and(tfm_check_sum<>0)and@|
- (font_check_sum[f]<>tfm_check_sum) then
- begin err_print('DVItoMP warning: Checksum mismatch for ');
-@.Checksum mismatch@>
- err_print_font(f);
- if history=spotless then history:=cksum_trouble;
- end;
-end
-
-@* Low level output routines.
-One of the basic output operations is to write a \MP\ string expression for
-a sequence of characters to be typeset. The main difficulties are that such
-strings can contain arbitrary eight-bit bytes and there is no fixed limit on
-the length of the string that needs to be produced. In extreme cases this
-can lead to expressions such as
-$$\vcenter{
- \hbox{\.{char7\&char15\&char31\&"?FWayzz"}}
- \hbox{\.{\&"zzaF"\&char15\&char3\&char31}}
- \hbox{\.{\&"Nxzzzzzzzwvtsqo"}}}
-$$
-
-@ A global variable |state| keeps track of the output process.
-When |state=normal| we have begun a quoted string and the next character
-should be a printable character or a closing quote. When |state=special|
-the last thing printed was a ``\.{char}'' construction or a closing quote
-and an ampersand should come next. The starting condition |state=initial|
-is a lot like |state=special|, except no ampersand is required.
-
-@d special=0 {the |state| after printing a ``\.{char}'' expression}
-@d normal=1 {the |state| value in a quoted string}
-@d initial=2 {initial |state|}
-
-@<Glob...@>=
-state:special..initial; {controls the process of printing a string}
-print_col:0..line_length;
- {there are at most this many characters on the current line}
-
-@ To print a string on the \.{MPX} file, initialize |print_col|, ensure that
-|state=initial|, and pass the characters one-at-a-time to |print_char|.
-
-@<Declare subroutines for printing strings@>=
-procedure print_char(@!c:eight_bits);
-var @!printable:boolean; {is it safe to print |xchr[c]|?}
-@!l:integer; {number of characters to print |c| or the \.{char} expression}
-begin printable:=(c>=" ")and(c<="~")and(c<>"""");
-if printable then l:=1
-else if c<10 then l:=5
-else if c<100 then l:=6
-else l:=7;
-if print_col+l>line_length-2 then
- begin if state=normal then
- begin print('"'); state:=special;
- end;
- print_ln(' ');
- print_col:=0;
- end;
-@<Print |c| and update |state| and |print_col|@>;
-end;
-
-@ @<Print |c| and update |state| and |print_col|@>=
-if state=normal then
- if printable then print(xchr[c])
- else begin print('"&char',c:1);
- print_col:=print_col+2;
- end
-else begin if state=special then
- begin print('&'); incr(print_col);
- end;
- if printable then
- begin print('"',xchr[c]); incr(print_col);
- end
- else print('char',c:1);
- end;
-print_col:=print_col+l;
-if printable then state:=normal @+else state:=special
-
-@ The |end_char_string| procedure gets the string ended properly and ensures
-that there is room for |l| more characters on the output line.
-
-@<Declare subroutines for printing strings@>=
-procedure end_char_string(@!l:integer);
-begin while state>special do
- begin print('"');
- incr(print_col);
- decr(state);
- end;
-if print_col+l>line_length then
- begin print_ln(' '); print_col:=0;
- end;
-state:=initial; {get ready to print the next string}
-end;
-
-@ Since |end_char_string| resets |state:=initial|, all we have to do is set
-|state:=initial| once at the beginning.
-
-@<Set init...@>=
-state:=initial;
-
-@ Characters and rules are positioned according to global variables |h| and~|v|
-as will be explained later. We also need scale factors that convert quantities
-to the right units when they are printed in the \.{MPX} file.
-
-Even though all variable names in the \MP\ output are made local via \.{save}
-commands, it is still desirable to preceed them with underscores. This makes
-the output more likely to work when used in a macro definition, since the
-generated variables names must not collide with formal parameters in such
-cases.
-
-@<Glob...@>=
-@!h,@!v:integer; {the current position in \.{DVI} units}
-@!conv:real; {converts \.{DVI} units to \MP\ points}
-@!mag:real; {magnification factor times 1000}
-
-@ @p @<Declare a procedure called |finish_last_char|@>@;
-procedure do_set_char(@!f,@!c:integer);
-begin if (c<font_bc[f])or(c>font_ec[f]) then
- abort('attempt to typeset invalid character ',c:1);
-@.attempt to typeset...@>
-if (h<>str_h2)or(v<>str_v)or(f<>str_f)or(dvi_scale<>str_scale) then
- begin if str_f>=0 then finish_last_char
- else if not fonts_used then
- @<Prepare to output the first character on a page@>;
- if not font_used[f] then
- @<Prepare to use font |f| for the first time on a page@>;
- print('_s('); print_col:=3;@/
- str_scale:=dvi_scale; str_f:=f; str_v:=v; str_h1:=h;
- end;
-print_char(c);
-str_h2:=h+@<Width of character |c| in font |f|@>;
-end;
-
-@ @<Glob...@>=
-@!font_used:array[0..max_fonts] of boolean;
- {has this font been used on this page?}
-@!fonts_used:boolean; {has any font been used on this page?}
-@!rules_used:boolean; {has any rules been set on this page?}
-@!str_h1,str_v:integer; {starting position for current output string}
-@!str_h2:integer; {where the current output string ends}
-@!str_f:integer; {internal font number for the current output string}
-@!str_scale:real; {value of |dvi_scale| for the current output string}
-
-@ The |font_used| array is not initialized until it is actually time to output
-a character.
-
-@<Prepare to output the first character on a page@>=
-begin k:=0;
-while (k<nf) do
- begin font_used[k]:=false; incr(k);
- end;
-fonts_used:=true;
-print_ln('string _n[];');
-print_ln('vardef _s(expr _t,_f,_m,_x,_y)=');
-print_ln(' addto _p also _t infont _f scaled _m shifted (_x,_y); enddef;');
-end
-
-@ @<Do any other initialization required for the new font |f|@>=
-font_used[f]:=false;
-
-@ @<Prepare to use font |f| for the first time on a page@>=
-begin font_used[f]:=true;
-print('_n',f:1,'=');
-print_col:=6;
-print_font(f);
-end_char_string(1);
-print_ln(';');
-end
-
-@ We maintain the invariant that |str_f=-1| when there is no output string
-under construction.
-
-@<Declare a procedure called |finish_last_char|@>=
-procedure finish_last_char;
-var @!m,@!x,@!y:real;
- {font scale factor and \MP\ coordinates of reference point}
-begin if str_f>=0 then
- begin m:=str_scale*font_scaled_size[str_f]*mag/font_design_size[str_f];@/
- x:=conv*str_h1; y:=conv*(-str_v);
- if (abs(x)>=4096.0)or(abs(y)>=4096.0)or(m>=4096.0)or(m<0) then
- begin warn('text scaled ',m:1:1,@|
- ' at (',x:1:1,',',y:1:1,') is out of range');
- end_char_string(60);
- end
- else end_char_string(40);
- print_ln(',_n',str_f:1,',',m:1:5,',',x:1:4,',',y:1:4,');');
- str_f:=-1;
- end;
-end;
-
-@ Setting rules is fairly simple.
-
-@p procedure do_set_rule(@!ht,@!wd:integer);
-var @!xx1,@!yy1,@!xx2,@!yy2,@!ww:real;
- {\MP\ coordinates of lower-left and upper-right corners}
-begin if wd=1 then @<Handle a special rule that determines the box size@>
-else if (ht>0)or(wd>0) then
- begin if str_f>=0 then finish_last_char;
- if not rules_used then
- begin rules_used:=true;@/
- print_ln('interim linecap:=0;');@/
- print_ln('vardef _r(expr _a,_w) =');
- print_ln(' addto _p doublepath _a withpen pencircle scaled _w enddef;');
- end;
- @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke
- and |ww| the desired stroke width@>;
- if (abs(xx1)>=4096.0)or(abs(yy1)>=4096.0)or@|
- (abs(xx2)>=4096.0)or(abs(yy2)>=4096.0)or(ww>=4096.0) then
- warn('hrule or vrule near (',xx1:1:1,',',yy1:1:1,') is out of range');
- print_ln('_r((',xx1:1:4,',',yy1:1:4,')..(',xx2:1:4,',',yy2:1:4,
- '), ',ww:1:4,');');
- end;
-end;
-
-@ @<Make |(xx1,yy1)| and |(xx2,yy2)| then ends of the desired penstroke...@>=
-xx1:=conv*h;
-yy1:=conv*(-v);
-if wd>ht then
- begin xx2:=xx1+conv*wd;
- ww:=conv*ht;@/
- yy1:=yy1+0.5*ww;
- yy2:=yy1;
- end
-else begin yy2:=yy1+conv*ht;
- ww:=conv*wd;@/
- xx1:=xx1+0.5*ww;
- xx2:=xx1;
- end
-
-@ Rules of width one dvi unit are not typeset since \.{MPtoTeX} adds an
-extraneous rule of this width in order to allow \.{DVItoMP} to deduce the
-dimensions of the boxes it ships out. The box width is the left edge of the
-last such rule; the height and depth are at the top and bottom of the rule.
-There should be only one special rule per picture but there could be more if
-the user tries to typeset his own one-dvi-unit rules. In this case the
-dimension-determining rule is the last one in the picture.
-
-@<Handle a special rule that determines the box size@>=
-begin pic_wd:=h; pic_dp:=v; pic_ht:=ht-v;
-end
-
-@ @<Glob...@>=
-pic_dp, pic_ht, pic_wd: integer; {picture dimensions from special rule}
-
-@ The following initialization and clean-up is required. We do a little more
-initialization than is absolutely necessary since some compilers might complain
-if the variables are uninitialized when |do_set_char| tests them.
-
-@p procedure start_picture;
-begin fonts_used:=false;
-rules_used:=false;
-str_f:=-1;@/
-str_v:=0; str_h2:=0; str_scale:=1.0; {values don't matter}
-print_ln('begingroup save _p,_r,_s,_n; picture _p; _p=nullpicture;');
-end;
-@#
-procedure stop_picture;
-var @!w,@!h,@!dd:real; {width, height, negative depth in PostScript points}
-begin if str_f>=0 then finish_last_char;
-@<Print a \&{setbounds} command based on picture dimensions@>;
-print_ln('_p endgroup');
-end;
-
-@ @<Print a \&{setbounds} command based on picture dimensions@>=
-dd:=-pic_dp*conv;
-w:=conv*pic_wd; h:=conv*pic_ht;@/
-print_ln('setbounds _p to (0,',dd:1:4,')--(',w:1:4,',',dd:1:4,')--');
-print_ln(' (',w:1:4,',',h:1:4,')--(0,',h:1:4,')--cycle;')
-
-@* Translation to symbolic form.
-The main work of \.{DVItoMP} is accomplished by the |do_dvi_commands|
-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. It is also capable of executing the typesetting commands for
-a character in a virtual font.
-
-@ The definition of \.{DVI} files refers to six registers,
-$(h,v,w,x,y,z)$, which hold integer values in \.{DVI} units.
-These units come directly from the input file except they need to be
-rescaled when typesetting characters from a virtual font.
-The stack of $(h,v,w,x,y,z)$ values is represented by six arrays
-called |hstack|, \dots, |zstack|.
-
-@<Glob...@>=
-@!w,@!x,@!y,@!z:integer;
- {current state values (|h| and |v| have already been declared)}
-@!hstack,@!vstack,@!wstack,@!xstack,@!ystack,@!zstack:
- array [0..stack_size] of integer; {pushed down values in \.{DVI} units}
-@!stk_siz:integer; {the current stack size}
-@!dvi_scale:real; {converts units of current input source to \.{DVI} units}
-
-@ @<Do initialization required before starting a new page@>=
-dvi_scale:=1.0;
-stk_siz:=0;
-h:=0; v:=0
-
-@ Next, we need procedures to handle |push| and |pop| commands.
-
-@p procedure do_push;
-begin if stk_siz=stack_size then
- abort('DVItoMP capacity exceeded (stack size=',stack_size:1,')');
-@.DVItoMP capacity exceeded...@>
-hstack[stk_siz]:=h; vstack[stk_siz]:=v; wstack[stk_siz]:=w;
-xstack[stk_siz]:=x; ystack[stk_siz]:=y; zstack[stk_siz]:=z;
-incr(stk_siz);
-end;
-@#
-procedure do_pop;
-begin if stk_siz=0 then bad_dvi('attempt to pop empty stack')
-else begin decr(stk_siz);
- h:=hstack[stk_siz]; v:=vstack[stk_siz]; w:=wstack[stk_siz];
- x:=xstack[stk_siz]; y:=ystack[stk_siz]; z:=zstack[stk_siz];
- end;
-end;
-
-@ We need to define the |set_virtual_char| procedure now because it is
-mutually recursive with |do_dvi_commands|. This is really a supervisory
-@^recursion@>
-procedure that calls |do_set_char| or adjusts the input source to read
-typesetting commands for a character in a virtual font.
-
-@p procedure do_dvi_commands;forward;@t\2@>
-procedure set_virtual_char(@!f,@!c:integer);
-var @!old_scale:real; {original value of |dvi_scale|}
-@!old_buf_ptr:0..virtual_space; {original value of the input pointer |buf_ptr|}
-@!old_fbase,@!old_ftop:0..max_fnums;
- {originally applicable part of the |font_num| table}
-begin if fbase[f]=0 then do_set_char(f,c)
-else begin old_fbase:=cur_fbase; old_ftop:=cur_ftop;
- cur_fbase:=fbase[f]; cur_ftop:=ftop[f];@/
- old_scale:=dvi_scale;
- dvi_scale:=dvi_scale*font_scaled_size[f];
- old_buf_ptr:=buf_ptr;
- buf_ptr:=start_cmd(f)(c);@/
- do_push;
- do_dvi_commands;
- do_pop;@/
- buf_ptr:=old_buf_ptr;
- dvi_scale:=old_scale;
- cur_fbase:=old_fbase; cur_ftop:=old_ftop;
- end;
-end;
-
-@ Before we get into the details of |do_dvi_commands|, 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)
-
-@<Declare a function called |first_par|@>=
-function first_par(o:eight_bits):integer;
-begin first_par:=0; {for compiler warnings}
-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 the |do_dvi_commands| procedure.
-
-@p procedure do_dvi_commands;
-label 9999;
-var o:eight_bits; {operation code of the current command}
-@!p,@!q:integer; {parameters of the current command}
-@!cur_font:integer; {current internal font number}
-begin if (cur_fbase<cur_ftop) and (buf_ptr<virtual_space) then
- cur_font:=select_font(font_num[cur_ftop-1]) {select first local font}
-else cur_font:=max_fnums+1; {current font is undefined}
-w:=0; x:=0; y:=0; z:=0; {initialize the state variables}
-while true do @<Translate the next command in the \.{DVI} file;
- |goto 9999| if it was |eop|@>;
-9999: do_nothing;
-end;
-
-@ The multiway switch in |first_par|, above, was organized by the length
-of each command; the one in |do_dvi_commands| is organized by the semantics.
-
-@ @<Translate the next command...@>=
-begin o:=get_byte; p:=first_par(o);
-if eof(dvi_file) then bad_dvi('the DVI file ended prematurely');
-@.the DVI file ended prematurely@>
-if o<set1+4 then {|set_char_0| through |set_char_127|, |set1| through |set4|}
- begin if cur_font>max_fnums then begin
- if vf_reading then
- abort('no font selected for character ',p:1,' in virtual font')
- else bad_dvi('no font selected for character ',p:1);
- end;
-@.no font selected@>
- set_virtual_char(cur_font,p);
- h:=h+@<Width of character |p| in font |cur_font|@>;
- end
-else case o of
- four_cases(put1): set_virtual_char(cur_font,p);
- set_rule: begin q:=trunc(signed_quad*dvi_scale);
- do_set_rule(trunc(p*dvi_scale),q);
- h:=h+q;
- end;
- put_rule: do_set_rule(trunc(p*dvi_scale),trunc(signed_quad*dvi_scale));
- @t\4@>@<Additional cases for translating \.{DVI} command |o| with
- first paramter |p|@>@;
- undefined_commands:bad_dvi('undefined command ',o:1);
-@.undefined command@>
- end; {all cases have been enumerated}
-end
-
-@ @<Additional cases for translating \.{DVI} command |o|...@>=
-four_cases(xxx1): for k:=1 to p do
- down_the_drain:=get_byte;
-pre,post,post_post: bad_dvi('preamble or postamble within a page!');
-@.preamble or postamble within a page@>
-
-@ @<Additional cases for translating \.{DVI} command |o|...@>=
-nop: do_nothing;
-bop: bad_dvi('bop occurred before eop');
-@.bop occurred before eop@>
-eop: goto 9999;
-push: do_push;
-pop: do_pop;
-
-@ @<Additional cases for translating \.{DVI} command |o|...@>=
-four_cases(right1):h:=h+trunc(p*dvi_scale);
-w0,four_cases(w1):begin w:=trunc(p*dvi_scale); h:=h+w;
- end;
-x0,four_cases(x1):begin x:=trunc(p*dvi_scale); h:=h+x;
- end;
-four_cases(down1):v:=v+trunc(p*dvi_scale);
-y0,four_cases(y1):begin y:=trunc(p*dvi_scale); v:=v+y;
- end;
-z0,four_cases(z1):begin z:=trunc(p*dvi_scale); v:=v+z;
- end;
-
-@ @<Additional cases for translating \.{DVI} command |o|...@>=
-sixty_four_cases(fnt_num_0),four_cases(fnt1):
- cur_font:=select_font(p);
-four_cases(fnt_def1): define_font(p);
-
-@* The main program.
-Now we are ready to put it all together. This is where \.{DVItoMP} starts,
-and where it ends.
-
-@p begin initialize; {get all variables initialized}
-@<Process the preamble@>;
-open_mpx_file;
-print_ln(banner);
-begin while true do
- begin @<Advance to the next |bop| command@>;
- for k:=0 to 10 do down_the_drain:=signed_quad;
- @<Do initialization required before starting a new page@>;
- start_picture;
- do_dvi_commands;
- if stk_siz<>0 then bad_dvi('stack not empty at end of page');
-@.stack not empty...@>
- stop_picture;
- print_ln('mpxbreak');
- end;
-done:end;
-final_end:end.
-
-@ The main program needs a few global variables in order to do its work.
-
-@<Glob...@>=
-@!k,@!p:integer; {general purpose registers}
-@!numerator,@!denominator:integer; {stated conversion ratio}
-
-@ @<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
- warn('identification in byte 1 should be ',id_byte:1,'!');
-@.identification...should be n@>
-@<Compute the conversion factor@>;
-p:=get_byte; {fetch the length of the introductory comment}
-while p>0 do
- begin decr(p); down_the_drain:=get_byte;
- end
-
-@ 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. No such adjustment is needed for
-|dvi_per_fix| since it is used to convert design sizes.
-
-@<Compute the conversion factor@>=
-numerator:=signed_quad; denominator:=signed_quad;
-if (numerator<=0)or(denominator<=0) then
- bad_dvi('bad scale ratio in preamble');
-@.bad scale ratio@>
-mag:=signed_quad/1000.0;
-if mag<=0.0 then bad_dvi('magnification isn''t positive');
-@.magnification isn't positive@>
-conv:=(numerator/254000.0)*(72.0/denominator)*mag;
-dvi_per_fix:=(254000.0/numerator)*(denominator/72.27)/1048576.0;
-
-@ @<Advance to the next |bop| command@>=
-repeat k:=get_byte;
-if (k>=fnt_def1)and(k<fnt_def1+4) then
- begin p:=first_par(k); define_font(p); k:=nop;
- end;
-until k<>nop;
-if k=post then goto done;
-if k<>bop then bad_dvi('missing bop');
-@.missing bop@>
-
-
-@* System-dependent changes.
-This section should be replaced, if necessary, by changes to the program
-that are necessary to make \.{DVItoMP} 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/Build/source/texk/web2c/man/Makefile.am b/Build/source/texk/web2c/man/Makefile.am
index 73e036b0966..77c1a324b19 100644
--- a/Build/source/texk/web2c/man/Makefile.am
+++ b/Build/source/texk/web2c/man/Makefile.am
@@ -15,7 +15,8 @@ web_man_sources = bibtex.man dvicopy.man dvitomp.man dvitype.man gftodvi.man \
gftopk.man gftype.man mft.man patgen.man pktogf.man pktype.man \
pltotf.man pooltype.man tftopl.man vftovp.man vptovf.man weave.man
-man_sources += $(mpware_man_sources)
+## Obsolete mpware manpages
+## man_sources += $(mpware_man_sources)
EXTRA_DIST += $(mpware_man_sources)
mpware_man_sources = dmp.man makempx.man mpto.man newer.man
@@ -43,10 +44,10 @@ endif ETEX
EXTRA_DIST += etex.man
if PDFTEX
-man_sources += $(pdeftex_man_sources)
+man_sources += $(pdftex_man_sources)
endif PDFTEX
-EXTRA_DIST += $(pdeftex_man_sources)
-pdeftex_man_sources = pdftex.man pdftosrc.man
+EXTRA_DIST += $(pdftex_man_sources)
+pdftex_man_sources = pdftex.man pdftosrc.man
if LUATEX
man_sources +=
diff --git a/Build/source/texk/web2c/man/Makefile.in b/Build/source/texk/web2c/man/Makefile.in
index a7a7d9811e1..860816b90b8 100644
--- a/Build/source/texk/web2c/man/Makefile.in
+++ b/Build/source/texk/web2c/man/Makefile.in
@@ -39,7 +39,7 @@ host_triplet = @host@
@MFN_TRUE@@MF_TRUE@am__append_4 =
@MP_TRUE@am__append_5 = mpost.man
@ETEX_TRUE@am__append_6 = etex.man
-@PDFTEX_TRUE@am__append_7 = $(pdeftex_man_sources)
+@PDFTEX_TRUE@am__append_7 = $(pdftex_man_sources)
@LUATEX_TRUE@am__append_8 =
@XETEX_TRUE@am__append_9 =
@OTANGLE_TRUE@am__append_10 =
@@ -283,14 +283,14 @@ top_srcdir = @top_srcdir@
wlibs = @wlibs@
x_ext_lib = @x_ext_lib@
x_tool_libs = @x_tool_libs@
-man_sources = $(bootstrap_man_sources) $(am__append_1) \
- $(mpware_man_sources) $(am__append_2) $(am__append_3) \
- $(am__append_4) $(am__append_5) $(am__append_6) \
- $(am__append_7) $(am__append_8) $(am__append_9) \
- $(am__append_10) $(am__append_11) $(misc_man_sources)
+man_sources = $(bootstrap_man_sources) $(am__append_1) $(am__append_2) \
+ $(am__append_3) $(am__append_4) $(am__append_5) \
+ $(am__append_6) $(am__append_7) $(am__append_8) \
+ $(am__append_9) $(am__append_10) $(am__append_11) \
+ $(misc_man_sources)
EXTRA_DIST = $(bootstrap_man_sources) $(web_man_sources) \
$(mpware_man_sources) tex.man mf.man mpost.man etex.man \
- $(pdeftex_man_sources) aleph.man $(misc_man_sources) \
+ $(pdftex_man_sources) aleph.man $(misc_man_sources) \
amslatex.min initex.min lamstex.min pdftexman.m4 slitex.min \
spell.add virtex.min
bootstrap_man_sources = cweb.man tangle.man
@@ -299,7 +299,7 @@ web_man_sources = bibtex.man dvicopy.man dvitomp.man dvitype.man gftodvi.man \
pltotf.man pooltype.man tftopl.man vftovp.man vptovf.man weave.man
mpware_man_sources = dmp.man makempx.man mpto.man newer.man
-pdeftex_man_sources = pdftex.man pdftosrc.man
+pdftex_man_sources = pdftex.man pdftosrc.man
misc_man_sources = \
amstex.man \
eplain.man \
diff --git a/Build/source/texk/web2c/mp.ch b/Build/source/texk/web2c/mp.ch
deleted file mode 100644
index 0d914ee9a4f..00000000000
--- a/Build/source/texk/web2c/mp.ch
+++ /dev/null
@@ -1,2100 +0,0 @@
-% $Id: mp.ch,v 1.8 2005/03/18 19:49:05 taco Exp $
-% mp.ch for C compilation with web2c. Public domain.
-%
-% Derived from mf.ch and John Hobby's mp.ch (the change file for the C
-% version of mf).
-%
-% Numbers of the form [pp.nnn] refer to the corresponding part and
-% module number of mp.web as implementors who read this change file are
-% much better advised to use a current WEB listing of MetaPost.
-%
-% (more recent changes in ChangeLog)
-% Revision 0.62 Jan 4 '95 by John Hobby
-% (Changes that only affect the banner line are not listed here)
-%
-% Updated for web2c-6.1/kpathsea-2.6 Jan 29 '95 by Ulrik Vieth
-% according to the current version of mf.ch.
-%
-% - produce separate binaries for INIMP, VIRMF (no '-I' flag!)
-% - make a big version of MP by default (cf. TeX and METAFONT)
-% - allow any input characters (0..255) (cf. TeX and METAFONT)
-% - use web2c/kpathsea routines in file opening and searching
-%
-% Updated for Revision 0.63 Apr 12 '95 by Ulrik Vieth
-%
-% - fixed the parsing routines for ps_tab_file to be able to handle
-% comments and blank lines in dvipsk's version of psfonts.map.
-% - increased max_read_files to 30 to avoid running out of read files
-% when reading stops before reaching EOF. (Better solution: write
-% a 'closefrom' macro that reads to EOF, which causes file closing.)
-
-@x [0] WEAVE: print changes only.
-\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
-@y
-\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in}
-\let\maybe=\iffalse
-\def\title{\MP\ changes for C}
-\def\glob{13}\def\gglob{20, 25} % these are defined in module 1
-\font\mc=cmr9
-@z
-
-@x [1.7] Convert `debug..gubed' and `stat..tats' into #ifdefs.
-@d debug==@{ {change this to `$\\{debug}\equiv\null$' when debugging}
-@d gubed==@t@>@} {change this to `$\\{gubed}\equiv\null$' when debugging}
-@y
-@d debug==ifdef('TEXMF_DEBUG')
-@d gubed==endif('TEXMF_DEBUG')
-@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==ifdef('STAT')
-@d tats==endif('STAT')
-@z
-
-@x [1.8] Same, for `init..tini'.
-@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==ifdef('INIMP')
-@d tini==endif('INIMP')
-@z
-
-% [1.11] Compile-time constants. Although we only change a few of
-% these, listing them all makes the patch file for a big MetaPost simpler.
-% 16K for BSD I/O; file_name_size is set from the system constant.
-@x
-@<Constants...@>=
-@!mem_max=30000; {greatest index in \MP's internal |mem| array;
- must be strictly less than |max_halfword|;
- must be equal to |mem_top| in \.{INIMP}, 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}
-@!emergency_line_length=255;
- {\ps\ output lines can be this long in unusual circumstances}
-@!stack_size=30; {maximum number of simultaneous input sources}
-@!max_read_files=4; {maximum number of simultaneously open \&{readfrom} files}
-@!max_strings=2500; {maximum number of strings; must not exceed |max_halfword|}
-@!string_vacancies=9000; {the minimum number of characters that should be
- available for the user's identifier names and strings,
- after \MP's own error messages are stored}
-@!strings_vacant=1000; {the minimum number of strings that should be available}
-@!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 \MP's own strings, which is currently about 22000}
-@!font_max=50; {maximum font number for included text fonts}
-@!font_mem_size=10000; {number of words for \.{TFM} information for text fonts}
-@!file_name_size=40; {file names shouldn't be longer than this}
-@!pool_name='MPlib:MP.POOL ';
- {string of length |file_name_size|; tells where the string pool appears}
-@.MPlib@>
-@!ps_tab_name='MPlib:PSFONTS.MAP ';
- {string of length |file_name_size|; locates font name translation table}
-@!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}
-@y
-@d file_name_size == maxint
-@d ssup_error_line = 255
-@d ssup_max_strings = 32767 {max value allowed by \.{TANGLE}}
-
-@<Constants...@>=
-@!max_internal=300; {maximum number of internal quantities}
-@!emergency_line_length=255;
- {\ps\ output lines can be this long in unusual circumstances}
-@!stack_size=300; {maximum number of simultaneous input sources}
-@!max_read_files=30; {maximum number of simultaneously open \&{readfrom} files}
-@!strings_vacant=1000; {the minimum number of strings that should be available}
-@!font_max=50; {maximum font number for included text fonts}
-@!font_mem_size=10000; {number of words for \.{TFM} information for text fonts}
-@!pool_name=TEXMF_POOL_NAME;
- {string of length |file_name_size|; tells where the string pool appears}
-@!engine_name=TEXMF_ENGINE_NAME; {the name of this engine}
-@.MPlib@>
-@!ps_tab_name='psfonts.map';
- {string of length |file_name_size|; locates font name translation table}
-@!bistack_size=1500; {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=15000; {maximum number of ligature/kern steps, must be
- at least 255 and at most 32510}
-@!max_kerns=2500; {maximum number of distinct kern amounts}
-@!max_font_dimen=50; {maximum number of \&{fontdimen} parameters}
-@#
-@!inf_main_memory = 3000;
-@!sup_main_memory = 8000000;
-
-@!inf_max_strings = 2500;
-@!sup_max_strings = ssup_max_strings;
-
-@!inf_buf_size = 500;
-@!sup_buf_size = 300000;
-
-@!inf_path_size = 1000;
-@!sup_path_size = 300000;
-
-@!inf_pool_size = 32000;
-@!sup_pool_size = 10000000;
-@!inf_pool_free = 1000;
-@!sup_pool_free = sup_pool_size;
-@!inf_string_vacancies = 8000;
-@!sup_string_vacancies = sup_pool_size - 23000;
-@z
-
-@x [1.12] Constants defined as WEB macros.
-@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 \.{INIMP};
- 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}
-@d max_write_files=4 {maximum number of simultaneously open \&{write} files}
-@y
-@d mem_min=0 {smallest index in the |mem| array, must not be less
- than |min_halfword|}
-@d hash_size=9500 {maximum number of symbolic tokens,
- must be less than |max_halfword-3*param_size|}
-@d hash_prime=7919 {a prime number equal to about 85\pct! of |hash_size|}
-@d max_in_open=25 {maximum number of input files and error insertions that
- can be going on simultaneously}
-@d param_size=1500 {maximum number of simultaneous macro parameters}
-@d max_write_files=10 {maximum number of simultaneously open \&{write} files}
-@z
-
-@x [1.13] Global parameters that can be changed in texmf.cnf.
-@<Glob...@>=
-@!bad:integer; {is some ``constant'' wrong?}
-@y
-@<Glob...@>=
-@!bad:integer; {is some ``constant'' wrong?}
-@#
-@!init
-@!ini_version:boolean; {are we \.{INIMP}? Set in \.{lib/texmfmp.c}}
-@!dump_option:boolean; {was the dump name option used?}
-@!dump_line:boolean; {was a \.{\%\AM mem} line seen?}
-tini@/
-@#
-@!bound_default:integer; {temporary for setup}
-@!bound_name:^char; {temporary for setup}
-@#
-@!main_memory:integer; {total memory words allocated in initex}
-@!mem_top:integer; {largest index in the |mem| array dumped by \.{INIMP};
- must be substantially larger than |mem_min|
- and not greater than |mem_max|}
-@!extra_mem_top:integer; {|mem_max:=mem_top+extra_mem_top| except in \.{INIMP}}
-@!mem_max:integer; {greatest index in \MP's internal |mem| array;
- must be strictly less than |max_halfword|;
- must be equal to |mem_top| in \.{INIMP}, otherwise |>=mem_top|}
-@!error_line:integer; {width of context lines on terminal error messages}
-@!half_error_line:integer; {width of first lines of contexts in terminal
- error messages; should be between 30 and |error_line-15|}
-@!max_print_line:integer; {width of longest text lines output;
- should be at least 60}
-@!pool_size:integer; {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 \MP's own strings, which is currently about 22000}
-@!string_vacancies:integer; {the minimum number of characters that should be
- available for the user's identifier names and strings,
- after \MP's own error messages are stored}
-@!buf_size:integer; {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|}
-@!path_size:integer; {maximum number of knots between breakpoints of a path}
-@!pool_free:integer;{minimum pool space free after format loaded}
-@!max_strings:integer; {maximum number of strings; must not exceed |max_halfword|}
-@!parse_first_line_p:c_int_type; {parse the first line for options}
-@!file_line_error_style_p:c_int_type; {file:line:error style messages.}
-@!eight_bit_p:c_int_type; {make all characters printable by default}
-@!halt_on_error_p:c_int_type; {stop at first error}
-@!quoted_filename:boolean; {current filename is quoted}
-@z
-
-@x [1.16] Use C macros for `incr' and `decr'.
-@d incr(#) == #:=#+1 {increase a variable by unity}
-@d decr(#) == #:=#-1 {decrease a variable by unity}
-@y
-@z
-
-% [2.19] The text_char type is used as an array index into xord. The
-% default type `char' produces signed integers, which are bad array
-% indices in C.
-@x
-@d text_char == char {the data type of characters in text files}
-@y
-@d text_char == ASCII_code {the data type of characters in text files}
-@z
-
-@x [2.22] Allow any character as input.
-@^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...@>=
-{Initialize |xchr| to the identity mapping.}
-for i:=0 to @'37 do xchr[i]:=i;
-for i:=@'177 to @'377 do xchr[i]:=i;
-@z
-
-@x [2.23]
-for i:=0 to @'176 do xord[xchr[i]]:=i;
-@y
-for i:=0 to @'176 do xord[xchr[i]]:=i;
-{Set |xprn| for printable ASCII, unless |eight_bit_p| is set.}
-for i:=0 to 255 do xprn[i]:=(eight_bit_p or ((i>=" ")and(i<="~")));
-
-{The idea for this dynamic translation comes from the patch by
- Libor Skarvada \.{<libor@@informatics.muni.cz>}
- and Petr Sojka \.{<sojka@@informatics.muni.cz>}. I didn't use any of the
- actual code, though, preferring a more general approach.}
-
-{This sets the |xchr|, |xord|, and |xprn| arrays. We overwrite the
- |xchr| assignments from module 21, just in case someone wants to do strange
- character translations. See the function definition in \.{texmfmp.c} for
- more comments.}
-if translate_filename then read_tcx_file;
-@z
-
-% [3.25] Declare name_of_file as a C string. See comments in tex.ch for
-% why we change the element type to text_char.
-@x
-@!name_of_file:packed array[1..file_name_size] of char;@;@/
- {on some systems this may be a \&{record} variable}
-@y
-@!name_of_file:^text_char;
-@z
-
-@x [3.26] Do file opening in C.
-@ The \ph\ compiler with which the original version of \MF\ was prepared
-extends 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
-\MP\ to undertake appropriate corrective action.
-@:PASCAL H}{\ph@>
-@^system dependencies@>
-
-\MP'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
-@ All of the file opening functions are defined in C.
-@d no_file_path = -1
-@z
-
-@x [3.27] Do file closing in C.
-@ 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;
-@y
-@ And all the file closing routines as well.
-@z
-
-@x [3.29] l.888 - Array size of input buffer is determined at runtime.
-@!buffer:array[0..buf_size] of ASCII_code; {lines of characters being read}
-@y
-@!buffer:^ASCII_code; {lines of characters being read}
-@z
-
-@x [3.30] Do `input_ln' in C.
-Standard \PASCAL\ says that a file should have |eoln| immediately
-before |eof|, but \MP\ 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
-We define |input_ln| in C, for efficiency. Nevertheless we quote the module
-`Report overflow of the input buffer, and abort' here in order to make
-\.{WEAVE} happy.
-
-@p @{ @<Report overflow of the input buffer, and abort@> @}
-@z
-
-@x [3.31] `term_in' and `term_out' are standard input and output.
-@<Glob...@>=
-@!term_in:alpha_file; {the terminal as an input file}
-@!term_out:alpha_file; {the terminal as an output file}
-@y
-@d term_in==stdin {the terminal as an input file}
-@d term_out==stdout {the terminal as an output file}
-@z
-
-@x [3.32] We don't need to open the terminal files.
-@ 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}
-@y
-@ Here is how to open the terminal files. |t_open_out| does nothing.
-|t_open_in|, on the other hand, does the work of ``rescanning,'' or getting
-any command line arguments the user has provided. It's defined in C.
-
-@d t_open_out == {output already open for text output}
-@z
-
-@x [3.33] Flushing output.
-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}
-@y
-these operations can be specified with {\mc UNIX}. |update_terminal|
-does an |fflush| (via the macro |flush|). |clear_terminal| is redefined
-to do nothing, since the user should control the terminal.
-@^system dependencies@>
-
-@d update_terminal == fflush(term_out)
-@d clear_terminal == do_nothing
-@z
-
-@x [3.36] Reading the command line.
-@ 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.
-Iff anything has been specified on the command line, then |t_open_in|
-will return with |last > first|.
-@^system dependencies@>
-
-@p
-function init_terminal:boolean; {gets the terminal input started}
-label exit;
-begin
- t_open_in;
- if last > first then begin
- loc := first;
- while (loc < last) and (buffer[loc]=' ') do
- incr(loc);
- if loc < last then begin
- init_terminal := true;
- goto exit;
- 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.');
- end;
-exit:
-end;
-@z
-
-@x [4.38] Dynamically allocate pool arrays.
-@!str_pool:packed array[pool_pointer] of pool_ASCII_code; {the characters}
-@!str_start : array[str_number] of pool_pointer; {the starting pointers}
-@!next_str : array[str_number] of str_number; {for linking strings in order}
-@y
-@!str_pool:^pool_ASCII_code; {the characters}
-@!str_start : ^pool_pointer; {the starting pointers}
-@!next_str : ^str_number; {for linking strings in order}
-@z
-
-@x [4.44] One more array.
-@!str_ref:array[str_number] of 0..max_str_ref;
-@y
-@!str_ref:^str_ref_type; {web2c only does |^identifier|}
-@z
-
-@x [4.64] l.1430 -- Do not hardwire printable ASCII.
- (k<" ")or(k>"~")
-@y
- not xprn[k]
-@z
-
-@x [5.69] error_line is a variable, so can't be a subrange array bound
-@!trick_buf:array[0..error_line] of ASCII_code; {circular buffer for
-@y
-@!trick_buf:array[0..ssup_error_line] of ASCII_code; {circular buffer for
-@z
-
-@x [5.76] Print rest of banner, eliminate misleading `no preloaded'
-@ By popular demand, \MP\ prints the banner line only on the transcript file.
-Thus there is nothing special to be printed here.
-
-@<Initialize the output...@>=
-update_terminal;
-@y
-@ Here is the very first thing that \MP\ 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 mem identifier together will occupy at most |max_print_line|
-character positions.
-
-@<Initialize the output...@>=
-wterm (banner);
-wterm (version_string);
-if mem_ident>0 then print(mem_ident); print_ln;
-if translate_filename then begin
- wterm('(');
- fputs(translate_filename, stdout);
- wterm_ln(')');
-end;
-update_terminal;
-@z
-
-@x [6.83] l.1815 - Add unspecified_mode.
-@d error_stop_mode=3 {stops at every opportunity to interact}
-@y
-@d error_stop_mode=3 {stops at every opportunity to interact}
-@d unspecified_mode=4 {extra value for command-line switch}
-@z
-
-@x [6.83] l.1825 - file:line:error style messages.
- print_nl("! "); print(#);
-@y
- if (file_line_error_style_p and not terminal_input) then
- begin
- print_nl ("");
- print (full_source_filename_stack[in_open]);
- print (":"); print_int (line); print (": ");
- print (#);
- end
- else begin print_nl("! "); print(#) end;
-@z
-
-@x [6.83] l.1822 - Add interaction_option.
-@!interaction:batch_mode..error_stop_mode; {current level of interaction}
-@y
-@!interaction:batch_mode..error_stop_mode; {current level of interaction}
-@!interaction_option:batch_mode..unspecified_mode; {set from command line}
-@z
-
-@x [6.84] l.1824 - Allow override by command line switch.
-@ @<Set init...@>=interaction:=error_stop_mode;
-@y
-@ @<Set init...@>=if interaction_option=unspecified_mode then
- interaction:=error_stop_mode
-else
- interaction:=interaction_option;
-@z
-
-@x [6.90] Eliminate non-local goto.
-@<Error hand...@>=
-procedure jump_out;
-begin goto end_of_MP;
-end;
-@y
-@d do_final_end==begin
- update_terminal;
- ready_already:=0;
- if (history <> spotless) and (history <> warning_issued) then
- uexit(1)
- else
- uexit(0);
- end
-@<Error hand...@>=
-procedure jump_out;
-begin
-close_files_and_terminate;
-do_final_end;
-end;
-@z
-
-@x [6.92] l.1956 - halt on error?
-print_char("."); show_context;
-@y
-print_char("."); show_context;
-if (halt_on_error_p) then begin
- history:=fatal_error_stop; jump_out;
-end;
-@z
-
-@x [6.93] Handle the switch-to-editor option.
-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 |call_edit| 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 \MP\ 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@>
- print(input_stack[file_ptr].name_field);
- print(" at line "); print_int(true_line);@/
- interaction:=scroll_mode; jump_out;
-@y
-"E": if file_ptr>0 then
- begin
- edit_name_start:=str_start[edit_file.name_field];
- edit_name_length:=length(edit_file.name_field);
- edit_line:=true_line;
- jump_out;
-@z
-
-@x [7.111] Do half and halfp in cpascal.h.
-@d half(#)==(#) div 2
-@d halfp(#)==(#) div 2
-@y
-@z
-
-@x [7.122-7.130] Optionally replace make_fraction etc. with external routines.
-@p function make_fraction(@!p,@!q:integer):fraction;
-@y
-In the C version, there are external routines that use double precision
-floating point to simulate functions such as |make_fraction|. This is carefully
-done to be virtually machine-independent and it gives up to 12 times speed-up
-on machines with hardware floating point. Since some machines do not have fast
-double-precision floating point, we provide a C preprocessor switch that allows
-selecting the standard versions given below.
-
-@p ifdef('FIXPT')@/
-function make_fraction(@!p,@!q:integer):fraction;
-@z
-@x
- if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n;
- end;
-end;
-@y
- if negative then make_fraction:=-(f+n)@+else make_fraction:=f+n;
- end;
-end;@/
-endif('FIXPT')
-@z
-@x
-@p function take_fraction(@!q:integer;@!f:fraction):integer;
-@y
-@p ifdef('FIXPT')@/
-function take_fraction(@!q:integer;@!f:fraction):integer;
-@z
-@x
-else take_fraction:=n+p;
-end;
-@y
-else take_fraction:=n+p;
-end;@/
-endif('FIXPT')
-@z
-@x
-@p function take_scaled(@!q:integer;@!f:scaled):integer;
-@y
-@p ifdef('FIXPT')@/
-function take_scaled(@!q:integer;@!f:scaled):integer;
-@z
-@x
-else take_scaled:=n+p;
-end;
-@y
-else take_scaled:=n+p;
-end;@/
-endif('FIXPT')
-@z
-@x
-operands are positive. \ (This procedure is not used especially often,
-so it is not part of \MP's inner loop.)
-
-@p function make_scaled(@!p,@!q:integer):scaled;
-@y
-operands are positive. \ (This procedure is not used especially often,
-so it is not part of \MP's inner loop, but we might as well allow for
-an external C routine.)
-
-@p ifdef('FIXPT')@/
-function make_scaled(@!p,@!q:integer):scaled;
-@z
-@x
- if negative then make_scaled:=-(f+n)@+else make_scaled:=f+n;
- end;
-end;
-@y
- if negative then make_scaled:=-(f+n)@+else make_scaled:=f+n;
- end;
-end;@/
-endif('FIXPT')
-@z
-
-@x [7.134] Do floor_scaled, floor_unscaled, round_unscaled, round_fraction in C.
-@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 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;
-@y
-@z
-
-@x [9.168] Increase memory size.
-@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
-@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==@"FFFFFFF {largest allowable value in a |halfword|}
-@z
-
-@x [9.170] Don't bother to subtract zero.
-@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}
-@y
-@d ho(#)==#
-@d qo(#)==#
-@d qi(#)==#
-@z
-
-@x [9.171] memory_word is defined externally.
-@!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;
-@y
-@=#include "texmfmem.h";@>
-@z
-
-@x [9.174] mem is dynamically allocated.
-@!mem : array[mem_min..mem_max] of memory_word; {the big dynamic storage area}
-@y
-@!mem : ^memory_word; {the big dynamic storage area}
-@z
-
-@x [10.184] Fix an unsigned/signed problem in getnode.
-if r>p+1 then @<Allocate from the top of node |p| and |goto found|@>;
-@y
-if r>intcast(p+1) then @<Allocate from the top of node |p| and |goto found|@>;
-@z
-
-% [11.193] Change the word `free' so that it doesn't conflict with the
-% standard C library routine of the same name. Also change arrays that
-% use mem_max, since that's a variable now, effectively disabling the feature.
-@x
-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;
-@y
-are debugging.)
-
-@d free==free_arr
-
-@<Glob...@>=
-@!debug @!free: packed array [0..1] of boolean; {free cells}
-@t\hskip1em@>@!was_free: packed array [0..1] of boolean;
-@z
-
-@x [11.197] Eliminate unsigned comparisons to zero.
-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
-@y
-repeat if (p>=lo_mem_max) then clobbered:=true
- else if (rlink(p)>=lo_mem_max) then clobbered:=true
-@z
-
-@x [12.212] Do `fix_date_and_time' in C.
-@ The following procedure, which is called just before \MP\ 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 \MP\ 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 \MP\ 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 \MP\ 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
-
-@x Replace class with c_class to avoid C++ keyword.
-@d max_class=20 {the largest class number}
-@y
-@d max_class=20 {the largest class number}
-@d class==c_class
-@z
-
-@x [12.217] Allow tab and form feed as input.
-for k:=127 to 255 do char_class[k]:=invalid_class;
-@y
-for k:=127 to 255 do char_class[k]:=invalid_class;
-char_class[tab]:=space_class;
-char_class[form_feed]:=space_class;
-@z
-
-@x [18.300,301] l.6451 - Make path_size a configuration option.
-@!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}
-@y
-@!delta_x:^scaled; {knot differences}
-@!delta_y:^scaled; {knot differences}
-@!delta:^scaled; {knot differences}
-@!psi:^angle; {turning angles}
-
-@ @<Other local variables for |make_choices|@>=
-@!k,@!n:0..sup_path_size; {current and final knot numbers}
-@z
-
-@x [18.304] l.6544 - Make path_size a configuration option.
-@!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}
-@y
-@!theta:^angle; {values of $\theta_k$}
-@!uu:^fraction; {values of $u_k$}
-@!vv:^angle; {values of $v_k$}
-@!ww:^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..sup_path_size; {current knot number}
-@z
-
-@x [27.585] l.11443 - data for file:line:error style.
-@!mpx_name : array[0..max_in_open] of halfword;
-@y
-@!mpx_name : array[0..max_in_open] of halfword;
-@!source_filename_stack : ^str_number;
-@!full_source_filename_stack : ^str_number;
-@z
-
-@x [35.745] area and extension rules.
-@ 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. Note that these variables
-cannot be of type |pool_pointer| because a string pool compaction could occur
-while scanning a file name.
-
-@<Glob...@>=
-@!area_delimiter:integer;
- {most recent `\.>' or `\.:' relative to |str_start[str_ptr]|}
-@!ext_delimiter:integer; {the relevant `\..', if any}
-@y
-@ The file names we shall deal with 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. Note that these variables
-cannot be of type |pool_pointer| because a string pool compaction could occur
-while scanning a file name.
-
-@<Glob...@>=
-@!area_delimiter:integer; {most recent `\./' relative to |str_start[str_ptr]|}
-@!ext_delimiter:integer; {the relevant `\..', if any}
-@z
-
-@x [35.746] MP and MF area directories.
-@d MP_area=="MPinputs:"
-@.MPinputs@>
-@d MF_area=="MFinputs:"
-@.MFinputs@>
-@d MP_font_area=="TeXfonts:"
-@.TeXfonts@>
-@y
-In C, the default paths are specified separately.
-@z
-
-@x [35.747] filenames: quoted
-area_delimiter:=-1; ext_delimiter:=-1;
-@y
-area_delimiter:=-1; ext_delimiter:=-1; quoted_filename:=false;
-@z
-
-@x [35.748] (more_name) Generalize directory separators.
-begin if c=" " then more_name:=false
-else begin if (c=">")or(c=":") then
-@y
-begin
-if c="""" then begin
- quoted_filename:=not quoted_filename;
- more_name:=true;
- end
-else if ((c=" ")or(c=tab)) and stop_at_space and (not quoted_filename) then
- more_name:=false
-else begin
- if IS_DIR_SEP (c) then
-@z
-@x [still 35.748] Last (not first) . is extension.
- else if (c=".")and(ext_delimiter<0) then
-@y
- else if (c=".") then
-@z
-
-@x [35.749] (end_name) Quote name if necessary.
-procedure end_name;
-var a,@!n,@!e:pool_pointer; {length of area, name, and extension}
-begin e:=pool_ptr-str_start[str_ptr]; {total length}
-@y
-procedure end_name;
-var a,@!n,@!e:pool_pointer; {length of area, name, and extension}
-@!j,@!s,@!t:pool_pointer; {running indices}
-@!must_quote:boolean; {whether we need to quote a string}
-begin str_room(6); {room for quotes}
-if area_delimiter<>-1 then begin
- {maybe quote |cur_area|}
- must_quote:=false;
- s:=str_start[str_ptr];
- t:=str_start[str_ptr]+area_delimiter+1;
- j:=s;
- while (not must_quote) and (j<>t) do begin
- must_quote:=str_pool[j]=" "; incr(j);
- end;
- if must_quote then begin
- for j:=pool_ptr-1 downto t do str_pool[j+2]:=str_pool[j];
- str_pool[t+1]:="""";
- for j:=t-1 downto s do str_pool[j+1]:=str_pool[j];
- str_pool[s]:="""";
- if ext_delimiter<>-1 then ext_delimiter:=ext_delimiter+2;
- area_delimiter:=area_delimiter+2;
- pool_ptr:=pool_ptr+2;
- end;
- s:=str_start[str_ptr]+area_delimiter+1;
- end
-else begin
- s:=str_start[str_ptr];
- end;
-{maybe quote |cur_name|}
-if ext_delimiter=-1 then t:=pool_ptr else t:=str_start[str_ptr]+ext_delimiter;
-must_quote:=false;
-j:=s;
-while (not must_quote) and (j<>t) do begin
- must_quote:=str_pool[j]=" "; incr(j);
- end;
-if must_quote then begin
- for j:=pool_ptr-1 downto t do str_pool[j+2]:=str_pool[j];
- str_pool[t+1]:="""";
- for j:=t-1 downto s do str_pool[j+1]:=str_pool[j];
- str_pool[s]:="""";
- if ext_delimiter<>-1 then ext_delimiter:=ext_delimiter+2;
- pool_ptr:=pool_ptr+2;
- end;
-if ext_delimiter<>-1 then begin
- {maybe quote |cur_ext|}
- s:=str_start[str_ptr]+ext_delimiter;
- t:=pool_ptr;
- must_quote:=false;
- j:=s;
- while (not must_quote) and (j<>t) do begin
- must_quote:=str_pool[j]=" "; incr(j);
- end;
- if must_quote then begin
- str_pool[t+1]:="""";
- for j:=t-1 downto s do str_pool[j+1]:=str_pool[j];
- str_pool[s]:="""";
- pool_ptr:=pool_ptr+2;
- end;
- end;
-e:=pool_ptr-str_start[str_ptr]; {total length}
-@z
-
-@x [35.750] print_file_name
-begin print(a); print(n); print(e);
-@y
-var must_quote: boolean; {whether to quote the filename}
-@!j:pool_pointer; {index into |str_pool|}
-begin
-must_quote:=false;
-if a>0 then begin
- j:=str_start[a];
- while (not must_quote) and (j<>str_stop(a)) do begin
- must_quote:=str_pool[j]=" "; incr(j);
- end;
-end;
-if n>0 then begin
- j:=str_start[n];
- while (not must_quote) and (j<>str_stop(n)) do begin
- must_quote:=str_pool[j]=" "; incr(j);
- end;
-end;
-if e>0 then begin
- j:=str_start[e];
- while (not must_quote) and (j<>str_stop(e)) do begin
- must_quote:=str_pool[j]=" "; incr(j);
- end;
-end;
-if must_quote then print("""");
-if a>0 then
- for j:=str_start[a] to str_stop(a)-1 do
- if so(str_pool[j])<>"""" then
- print(so(str_pool[j]));
-if n>0 then
- for j:=str_start[n] to str_stop(n)-1 do
- if so(str_pool[j])<>"""" then
- print(so(str_pool[j]));
-if e>0 then
- for j:=str_start[e] to str_stop(e)-1 do
- if so(str_pool[j])<>"""" then
- print(so(str_pool[j]));
-if must_quote then print("""");
-@z
-
-@x [35.751] macro used in pack_file_name
-@d append_to_name(#)==begin c:=#; incr(k);
- if k<=file_name_size then name_of_file[k]:=xchr[c];
- end
-@y
-@d append_to_name(#)==begin c:=#; if not (c="""") then begin incr(k);
- if k<=file_name_size then name_of_file[k]:=xchr[c];
- end end
-@z
-
-@x [35.751] (pack_file_name) malloc and null terminate name_of_file.
-for j:=str_start[a] to str_stop(a)-1 do append_to_name(so(str_pool[j]));
-@y
-if name_of_file then libc_free (name_of_file);
-name_of_file := xmalloc_array (ASCII_code, length(a)+length(n)+length(e)+1);
-for j:=str_start[a] to str_stop(a)-1 do append_to_name(so(str_pool[j]));
-@z
-@x
-for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
-@y
-name_of_file[name_length + 1] := 0;
-@z
-
-@x [35.752] default mem area is nonexistent
-@d mem_default_length=15 {length of the |MP_mem_default| string}
-@d mem_area_length=6 {length of its area part}
-@y
-@d mem_area_length=0 {no fixed area in C}
-@z
-
-@x [35.753] Where `plain.mem' is.
-@!MP_mem_default:packed array[1..mem_default_length] of char;
-
-@ @<Set init...@>=
-MP_mem_default:='MPlib:plain.mem';
-@.MPlib@>
-@.plain@>
-@^system dependencies@>
-@y
-@!mem_default_length: integer;
-@!MP_mem_default: ^char;
-@!troff_mode:boolean; {has the user requested \.{troff} mode?}
-
-@ We set the name of the default format file and the length of that name
-in \.{texmfmp.c}, since we want them to depend on the name of the
-program.
-@z
-
-@x [35.755] Change to pack_buffered_name as with pack_file_name.
-for j:=1 to n do append_to_name(xord[MP_mem_default[j]]);
-@y
-if name_of_file then libc_free (name_of_file);
-name_of_file := xmalloc_array (ASCII_code, n+(b-a+1)+mem_ext_length+1);
-for j:=1 to n do append_to_name(xord[MP_mem_default[j]]);
-@z
-% @x [35.755] Set program name to match format.
-% for j:=a to b do append_to_name(buffer[j]);
-% @y
-% for j:=a to b do append_to_name(buffer[j]);
-% name_of_file[k+1]:=0;
-% kpse_reset_program_name(name_of_file+1);{set search path to match format}
-% @z
-@x [35.755] Change to pack_buffered_name as with pack_file_name.
-for k:=name_length+1 to file_name_size do name_of_file[k]:=' ';
-@y
-name_of_file[name_length + 1] := 0;
-@z
-
-@x [35.756] Mem file opening: only try once, with path searching.
- pack_buffered_name(0,loc,j-1); {try first without the system file area}
- if w_open_in(mem_file) then goto found;
- pack_buffered_name(mem_area_length,loc,j-1);
- {now try the system mem file area}
- if w_open_in(mem_file) then goto found;
-@y
- pack_buffered_name(0,loc,j-1);
- if w_open_in(mem_file) then goto found;
-@z
-
-@x [still 35.756] Replace `PLAIN' in error messages with `default'.
- wterm_ln('Sorry, I can''t find that mem file;',' will try PLAIN.');
-@y
- wterm ('Sorry, I can''t find the mem file `');
- fputs (stringcast(name_of_file + 1), stdout);
- wterm ('''; will try `');
- fputs (MP_mem_default + 1, stdout);
- wterm_ln ('''.');
-@z
-@x
- wterm_ln('I can''t find the PLAIN mem file!');
-@.I can't find PLAIN...@>
-@y
- wterm ('I can''t find the mem file `');
- fputs (MP_mem_default + 1, stdout);
- wterm_ln ('''!');
-@.I can't find the mem...@>
-@z
-
-@x [35.758] Make scan_file_name ignore leading tabs as well as spaces.
-@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
-
-@x [35.760] `logname' is declared in <unistd.h> on some systems.
-`\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's output files.
-@y
-`\.{.mem}' and `\.{.tfm}' in order to make the names of \MP's output files.
-@d log_name == texmf_log_name
-@z
-
-@x [35.763] prompt_file_name: avoid empty filenames.
-var @!k:0..buf_size; {index into |buffer|}
-@y
-var @!k:0..buf_size; {index into |buffer|}
-@!saved_cur_name:str_number; {to catch empty terminal input}
-@z
-
-@x [35.763] prompt_file_name: avoid empty filenames.
-clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
-if cur_ext="" then cur_ext:=e;
-@y
-saved_cur_name:=cur_name;
-clear_terminal; prompt_input(": "); @<Scan file name in the buffer@>;
-if cur_ext="" then cur_ext:=e;
-if length(cur_name)=0 then cur_name:=saved_cur_name;
-@z
-
-@x [35.764] <Scan file name...> needs similar leading tab treatment.
-@ @<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
-
-@x [35.765] Adjust for C string conventions.
-@!months:packed array [1..36] of char; {abbreviations of month names}
-@y
-@!months:^char;
-@z
-
-@x [35.765] Change name of recorder file.
-if job_name=0 then job_name:="mpout";
-pack_job_name(".log");
-@y
-if job_name=0 then job_name:=get_job_name("mpout");
-pack_job_name(".fls");
-recorder_change_filename(stringcast(name_of_file+1));
-pack_job_name(".log");
-@z
-
-@x [35.767]
-begin wlog(banner);
-print(mem_ident); print(" ");
-print_int(round_unscaled(internal[day])); print_char(" ");
-months:='JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
-@y
-begin wlog(banner);
-wlog (version_string);
-print(mem_ident); print(" ");
-print_int(round_unscaled(internal[day])); print_char(" ");
-months := ' JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC';
-@z
-
-@x [35.767] l.14341 - Print name of TCX, if given.
-end
-@y
-if translate_filename then begin
- wlog_cr;
- wlog('(');
- fputs(translate_filename, log_file);
- wlog(')');
-end;
-end
-@z
-
-@x [35.768] (try_extension) a_open_in of input file needs path specifier.
-begin pack_file_name(cur_name,cur_area,ext);
-in_name:=cur_name; in_area:=cur_area;
-if a_open_in(cur_file) then try_extension:=true
-else begin if str_vs_str(ext,".mf")=0 then in_area:=MF_area
- else in_area:=MP_area;
- pack_file_name(cur_name,in_area,ext);
- try_extension:=a_open_in(cur_file);
- end;
-@y
-{|cur_ext| will be \.{two} if the filename was \.{one.two}.}
-begin pack_file_name(cur_name,cur_area,cur_ext);
-in_name:=cur_name; in_area:=cur_area;
-{The extension is not relevant for determining whether we're allowed to
- open the file.}
-if not open_in_name_ok(stringcast(name_of_file+1)) then try_extension:=false
-else if str_vs_str(ext,".mf")=0 then
- try_extension:=a_open_in(cur_file, kpse_mf_format)
-else try_extension:=a_open_in(cur_file, kpse_mp_format);
-@z
-
-@x [35.770] We need another loop variable.
-@p procedure start_input; {\MP\ will \.{input} something}
-label done;
-@y
-@p procedure start_input; {\MP\ will \.{input} something}
-label done;
-var j:integer;
-@z
-
-@x [still 35.770] Kpathsea already tries with no extension.
- if cur_ext="" then
- if try_extension(".mp") then goto done
- else if try_extension("") then goto done
- else if try_extension(".mf") then goto done
- else do_nothing
- else if try_extension(cur_ext) then goto done;
-@y
- if try_extension(".mp") then goto done
- else if try_extension(".mf") then goto done
- else do_nothing;
-@z
-
-@x [still 35.770] Allow jobname to be `one.two'.
- begin job_name:=cur_name; str_ref[job_name]:=max_str_ref;
-@y
- begin
- j:=1;
- begin_name;
- stop_at_space:=false;
- while (j<=name_length)and(more_name(name_of_file[j])) do
- incr(j);
- stop_at_space:=true;
- end_name;
- job_name:=get_job_name(cur_name);
- str_ref[job_name] := max_str_ref;
-@z
-
-@x [35.771] Cannot return name to string pool, for the e option?
-flush_string(name); name:=cur_name; cur_name:=0
-@y
-@z
-
-@x [35.774] (copy_old_name) Allocate old_file_name dynamically.
-for j:=str_start[s] to str_stop(s)-1 do
-@y
-if old_file_name then libc_free (old_file_name);
-old_file_name := xmalloc_array (ASCII_code, length(s)+1);
-for j:=str_start[s] to str_stop(s)-1 do
-@z
-@x [still 35.774] Avoid blanking rest of nonexistent array.
-for k:=old_name_length+1 to file_name_size do @+old_file_name[k]:=' ';
-@y
-old_file_name[old_name_length + 1] := 0;
-@z
-
-@x [35.775] Declare old_file_name as a regular C string.
-@!old_file_name : packed array[1..file_name_size] of char;
-@y
-@!old_file_name : ^text_char;
-@z
-
-@x [35.776] [Unique to MP] Path selector for |a_open_in| of mpx file.
-if not a_open_in(cur_file) then
- begin end_file_reading;
-@y
-if not a_open_in(cur_file,no_file_path) then
- begin end_file_reading;
-@z
-
-@x [35.777] [Unique to MP] Invoke |makempx|.
-copy_old_name(name)
-{System-dependent code should be added here}
-@y
-copy_old_name (name);
-if not call_make_mpx (stringcast(old_file_name+1), stringcast(name_of_file+1))
- then goto not_found
-@z
-
-@x [35.778] [Unique to MP] Fix help message for our implementation.
- ("try running it manually through MPtoTeX, TeX, and DVItoMP");
-@y
- ("try running it manually through MPto -tex, TeX, and DVItoMP.");
-@z
-
-@x [35.782] [Unique to MP] Path selector for |a_open_in| of readfrom file.
-if not a_open_in(rd_file[n]) then goto not_found;
-@y
-if not open_in_name_ok(stringcast(name_of_file+1))
- or not a_open_in(rd_file[n],kpse_mp_format) then goto not_found;
-@z
-
-@x [35.783] The Amiga needs a different open_write_file.
-@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
-
-@p procedure open_write_file(s:str_number; n:readf_index);
-begin str_scan_file(s);
-pack_cur_name;
-while not a_open_out(wr_file[n]) do
- prompt_file_name("file name for write output","");
-wr_fname[n]:=s;
-add_str_ref(s);
-end;
-@y
-@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
-The Amiga operating system does not permit write access to a file that is
-currently opened in read mode. To avoid disaster, we look for the file to
-be opened in the list of |read_from| files and close it if present.
-
-@d amiga==ifdef('AMIGA')
-@d agima==endif('AMIGA')
-@f amiga==begin
-@f agima==end
-
-@p procedure open_write_file(s:str_number; n:readf_index);
-amiga@;
-label done;
-var @!n0:readf_index; {Scratch variable}
-agima@;
-begin
- str_scan_file(s);
- pack_cur_name;
-amiga@;
- for n0:=0 to read_files-1 do begin
- if rd_fname[n0]<>0 then begin
- if str_vs_str(s,rd_fname[n0])=0 then begin
- a_close(rd_file[n0]);
- delete_str_ref(rd_fname[n0]);
- rd_fname[n0]:=0;
- if n0=read_files-1 then read_files:=n0;
- goto done;
- end;
- end;
- end;
-done: do_nothing;
-agima@;
-while not open_out_name_ok(stringcast(name_of_file+1))
- or not a_open_out(wr_file[n]) do
- prompt_file_name("file name for write output","");
-wr_fname[n]:=s;
-add_str_ref(s);
-{If on first line of input, log file is not ready yet, so don't log.}
-if log_opened then begin
- old_setting:=selector;
- if (internal[tracing_online]<=0) then
- selector:=log_only {Show what we're doing in the log file.}
- else selector:=term_and_log; {Show what we're doing.}
- print_nl("write");
- print_int(n);
- print(" = `");
- print_file_name(cur_name,cur_area,cur_ext);
- print("'."); print_nl(""); print_ln;
- selector:=old_setting;
-end;
-end;
-@z
-
-@x [41.1040] if batchmode, MakeTeX... scripts should be silent.
-mode_command: begin print_ln; interaction:=cur_mod;
-@y
-mode_command: begin print_ln; interaction:=cur_mod;
-if interaction = batch_mode
-then kpse_make_tex_discard_errors := 1
-else kpse_make_tex_discard_errors := 0;
-@z
-
-@x [42.1151] Fix `threshold' conflict with local variable name.
-@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;
-@y
-@p function compute_threshold(@!m:integer):scaled;
-var @!d:scaled; {lower bound on the smallest interval size}
-begin excess:=min_cover(0)-m;
-if excess<=0 then compute_threshold:=0
-else begin repeat d:=perturbation;
- until min_cover(d+d)<=m;
- while min_cover(d)>m do d:=perturbation;
- compute_threshold:=d;
- end;
-end;
-@z
-
-@x [42.1152] Change the call to the threshold function.
-begin d:=threshold(m); perturbation:=0;
-@y
-begin d:=compute_threshold(m); perturbation:=0;
-@z
-
-@x [45.1164] Writing the tfm file.
-@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|}
-@y
-The default definitions don't work. Why not? So use C macros.
-
-@d tfm_out(#) == put_byte (#, tfm_file)
-@d tfm_two(#) == put_2_bytes (tfm_file, #)
-@d tfm_four(#) == put_4_bytes (tfm_file, #)
-
-@p procedure tfm_qqqq(@!x:four_quarters); {output four quarterwords to |tfm_file|}
-@z
-
-@x [42.1165] print_file_name
-print_nl("Font metrics written on "); print(metric_file_name); print_char(".");
-@y
-print_nl("Font metrics written on ");
-print_file_name(0,metric_file_name,0);
-print_char(".");
-@z
-
-% [43.1182] [See TeX module 564] Reading tfm files. As a special case,
-% whenever we open a tfm file for input, we read its first byte into
-% "tfm_temp" right away.
-@x
-@d tfget==get(tfm_infile)
-@d tfbyte==tfm_infile^
-@y
-@d tfget==tfm_temp:=getc(tfm_infile)
-@d tfbyte==tfm_temp
-@z
-
-% [43.1186] [See TeX module 575] We only want `eof' on the TFM file
-% to be true if we previously had EOF, not if we're at EOF now.
-% This is like `feof', and unlike our implementation of `eof' elsewhere.
-@x
-if eof(tfm_infile) then goto bad_tfm;
-@y
-if feof(tfm_infile) then goto bad_tfm;
-@z
-
-% [43.1188] [See TeX module 563] TFM file opening.
-@x
-if cur_area="" then cur_area:=MP_font_area;
-if cur_ext="" then cur_ext:=".tfm";
-pack_cur_name;
-@y
-if cur_ext="" then cur_ext:=".tfm";
-pack_cur_name;
-@z
-
-@x [43.1195] [Unique to MP] Path selector for |a_open_in| of ps_tab_file.
-begin name_of_file:=ps_tab_name;
-if a_open_in(ps_tab_file) then
-@y
-begin
-name_length := strlen (ps_tab_name);
-name_of_file := xmalloc_array (ASCII_code, name_length+1);
-strcpy (stringcast(name_of_file+1), ps_tab_name); {copy the string}
-if a_open_in(ps_tab_file, kpse_fontmap_format) then
-@z
-
-@x [43.1198] Allow blank lines and comment lines in |ps_tab_file|.
-@ @<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
-str_room(lmax);
-j:=lmax;
-loop @+begin if eoln(ps_tab_file) then
- fatal_error("The psfont map file is bad!");
- read(ps_tab_file,c);
- if c=' ' then goto done;
-@y
-@ If we encounter the end of line before we have started reading
-characters from |ps_tab_file|, we have found an entirely blank
-line and we skip over it. Otherwise, we abort if the line ends
-prematurely. If we encounter a comment character, we also skip
-over the line, since recent versions of \.{dvips} allow comments
-in the font map file.
-
-@<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
-str_room(lmax);
-j:=lmax;
-loop @+begin if eoln(ps_tab_file) then
- if j=lmax then begin flush_cur_string;
- goto common_ending; {skip over blank line}
- end
- else fatal_error("The psfonts.map file is bad!");
- read(ps_tab_file,c);
- if ((c='%')or(c='*')or(c=';')or(c='#')) then begin flush_cur_string;
- goto common_ending; {skip over comment line}
- end;
- if ((c=' ')or(c=tab)) then goto done;
-@z
-
-@x [43.1199] Allow tabs as field seperators in |ps_tab_file|.
-repeat if eoln(ps_tab_file) then fatal_error("The psfont map file is bad!");
- read(ps_tab_file,c);
-until c<>' ';
-repeat decr(j);
- if j<0 then fatal_error("The psfont map file is bad!");
- append_char(xord[c]);
- if eoln(ps_tab_file) then c:=' ' @+else read(ps_tab_file,c);
-until c=' ';
-@y
-repeat if eoln(ps_tab_file) then fatal_error("The psfonts.map file is bad!");
- read(ps_tab_file,c);
-until ((c<>' ')and(c<>tab));
-repeat decr(j);
- if j<0 then fatal_error("The psfonts.map file is bad!");
- append_char(xord[c]);
- if eoln(ps_tab_file) then c:=' ' @+else read(ps_tab_file,c);
-until ((c=' ')or(c=tab));
-@z
-
-@x [44.1208] l.21341 - i18n fix
- print(" output file");
- if total_shipped>1 then print_char("s");
-@y
- print(" output ");
- if total_shipped>1 then print("files")
- else print("file");
-@z
-
-@x [44.1208] print_file_name
- print(first_file_name);
-@y
- print_file_name(0,first_file_name,0);
-@z
-@x [44.1208 print_file_name
- print(last_file_name);
-@y
- print_file_name(0,last_file_name,0);
-@z
-
-@x [45.1279] INI = VIR.
-mem_ident:=" (INIMP)";
-@y
-if ini_version then mem_ident:=" (INIMP)";
-@z
-
-@x [45.1280] Add base_engine.
-@!w: four_quarters; {four ASCII codes}
-@y
-@!w: four_quarters; {four ASCII codes}
-@!base_engine: ^text_char;
-@z
-
-@x [45.1281] Add base_engine.
-@!w: four_quarters; {four ASCII codes}
-@y
-@!w: four_quarters; {four ASCII codes}
-@!base_engine: ^text_char;
-@!dummy_xord: ASCII_code;
-@!dummy_xchr: text_char;
-@!dummy_xprn: ASCII_code;
-@z
-
-@x [45.1282] Reading and writing of `mem_file' done in C.
-@d dump_wd(#)==begin mem_file^:=#; put(mem_file);@+end
-@d dump_int(#)==begin mem_file^.int:=#; put(mem_file);@+end
-@d dump_hh(#)==begin mem_file^.hh:=#; put(mem_file);@+end
-@d dump_qqqq(#)==begin mem_file^.qqqq:=#; put(mem_file);@+end
-@y
-@z
-
-@x [45.1283]
-@d undump_wd(#)==begin get(mem_file); #:=mem_file^;@+end
-@d undump_int(#)==begin get(mem_file); #:=mem_file^.int;@+end
-@d undump_hh(#)==begin get(mem_file); #:=mem_file^.hh;@+end
-@d undump_qqqq(#)==begin get(mem_file); #:=mem_file^.qqqq;@+end
-@y
-@z
-
-@x [45.1284]
-dump_int(@$);@/
-@y
-dump_int(@"57324D50); {Web2C \MP's magic constant: "W2MP"}
-{Align engine to 4 bytes with one or more trailing NUL}
-x:=strlen(engine_name);
-base_engine:=xmalloc_array(text_char,x+4);
-strcpy(stringcast(base_engine), engine_name);
-for k:=x to x+3 do base_engine[k]:=0;
-x:=x+4-(x mod 4);
-dump_int(x);dump_things(base_engine[0], x);
-libc_free(base_engine);@/
-dump_int(@$);@/
-dump_int(main_memory);@/
-@<Dump |xord|, |xchr|, and |xprn|@>;
-@z
-
-@x [45.1285] Avoid Pascal file convention.
-x:=mem_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;
-@y
-undump_int(x);
-if x<>@"57324D50 then goto off_base; {not a base file}
-undump_int(x);
-if (x<0) or (x>256) then goto off_base; {corrupted base file}
-base_engine:=xmalloc_array(text_char, x);
-undump_things(base_engine[0], x);
-base_engine[x-1]:=0; {force string termination, just in case}
-if strcmp(engine_name, stringcast(base_engine)) then
- begin wake_up_terminal;
- wterm_ln('---! ', stringcast(name_of_file+1), ' was written by ', base_engine);
- libc_free(base_engine);
- goto off_base;
-end;
-libc_free(base_engine);
-undump_int(x);
-if x<>@$ then begin {check that strings are the same}
- wake_up_terminal;
- wterm_ln('---! ', stringcast(name_of_file+1), ' doesn''t match pool strings');
- goto off_base;
-end;
-undump_int(x);
-if x<>main_memory then goto off_base;
-@<Undump |xord|, |xchr|, and |xprn|@>;
-undump_int(x);
-if x<>mem_min then goto off_base;
-{Dynamic allocation \`a la \.{mf.ch}.}
-@+init
-if ini_version then begin
- {We allocated these at start-up, but now we need to reallocate.}
- libc_free (mem);
- libc_free (str_ref);
- libc_free (next_str);
- libc_free (str_start);
- libc_free (str_pool);
-end;
-@+tini
-undump_int(mem_top);
-if mem_max < mem_top then mem_max:=mem_top; {Use at least what we dumped.}
-if mem_min+1100>mem_top then goto off_base;
-mem_max := mem_top + extra_mem_top;
-mem:=xmalloc_array (memory_word, mem_max - mem_min + 1);
-@z
-
-@x [45.1287] String pool undumping is dynamic.
-undump_size(0)(pool_size)('string pool size')(pool_ptr);
-undump_size(0)(max_strings-1)('max strings')(max_str_ptr);
-@y
-undump_size(0)(sup_pool_size-pool_free)('string pool size')(pool_ptr);
-if pool_size < pool_ptr + pool_free then
- pool_size := pool_ptr+pool_free;
-undump_size(0)(sup_max_strings)('max strings')(max_str_ptr);
-@/
-str_ref:=xmalloc_array (str_ref_type, max_strings);
-next_str:=xmalloc_array (str_number, max_strings);
-str_start:=xmalloc_array (pool_pointer, max_strings);
-str_pool:=xmalloc_array (pool_ASCII_code, pool_size);
-@z
-
-@x [45.1293] l.22667 - Allow command line to override dumped value.
-undump(batch_mode)(error_stop_mode)(interaction);
-@y
-undump(batch_mode)(error_stop_mode)(interaction);
-if interaction_option<>unspecified_mode then interaction:=interaction_option;
-@z
-
-@x [45.1293] eof is like feof here.
-undump_int(x);@+if (x<>69073)or eof(mem_file) then goto off_base
-@y
-undump_int(x);@+if (x<>69073)or feof(mem_file) then goto off_base
-@z
-
-@x [45.1294] Eliminate probably-wrong word `preloaded' from mem_idents.
-print(" (preloaded mem="); print(job_name); print_char(" ");
-print_int(round_unscaled(internal[year]) mod 100); print_char(".");
-@y
-print(" (mem="); print(job_name); print_char(" ");
-print_int(round_unscaled(internal[year])); print_char(".");
-@z
-
-@x [46.1298] Dynamic allocation.
-@p begin @!{|start_here|}
-@y
-@d const_chk(#) == begin if # < inf@&# then # := inf@&# else
- if # > sup@&# then # := sup@&# end
-{|setup_bound_var| stuff duplicated in \.{tex.ch}.}
-@d setup_bound_var(#) == bound_default := #; setup_bound_var_end
-@d setup_bound_var_end(#) == bound_name := #; setup_bound_var_end_end
-@d setup_bound_var_end_end(#) ==
- setup_bound_variable (address_of (#), bound_name, bound_default);
-
-@p begin @!{|start_here|}
- {See comments in \.{tex.ch} for why the name has to be duplicated.}
- setup_bound_var (250000)('main_memory')(main_memory);
- {|memory_word|s for |mem| in \.{INIMP}}
- setup_bound_var (0)('extra_mem_top')(extra_mem_top);
- {increase high mem in \.{VIRMP}}
- setup_bound_var (100000)('pool_size')(pool_size);
- setup_bound_var (75000)('string_vacancies')(string_vacancies);
- setup_bound_var (3000)('buf_size')(buf_size);
- setup_bound_var (2000)('path_size')(path_size);
- setup_bound_var (5000)('pool_free')(pool_free); {min pool avail after fmt}
- setup_bound_var (15000)('max_strings')(max_strings);
- setup_bound_var (79)('error_line')(error_line);
- setup_bound_var (50)('half_error_line')(half_error_line);
- setup_bound_var (79)('max_print_line')(max_print_line);
- if error_line > ssup_error_line then error_line := ssup_error_line;
-
- const_chk (main_memory);
-@+init
- if ini_version then
- extra_mem_top := 0;
-@+tini
- if extra_mem_top>sup_main_memory then extra_mem_top:=sup_main_memory;
- mem_top := mem_min + main_memory - 1;
- mem_max := mem_top;
-
- const_chk (pool_size);
- const_chk (string_vacancies);
- const_chk (buf_size);
- const_chk (path_size);
- const_chk (pool_free);
- const_chk (max_strings);
-
- buffer:=xmalloc_array (ASCII_code, buf_size);
- delta_x:=xmalloc_array (scaled, path_size);
- delta_y:=xmalloc_array (scaled, path_size);
- delta:=xmalloc_array (scaled, path_size);
- psi:=xmalloc_array (angle, path_size);
- theta:=xmalloc_array (angle, path_size);
- uu:=xmalloc_array (fraction, path_size);
- vv:=xmalloc_array (angle, path_size);
- ww:=xmalloc_array (fraction, path_size);
- source_filename_stack:=xmalloc_array (str_number, max_in_open);
- full_source_filename_stack:=xmalloc_array (str_number, max_in_open);
-
-@+init
-if ini_version then begin
- mem:=xmalloc_array (memory_word, mem_top - mem_min + 1);
-
- str_ref:=xmalloc_array (str_ref_type, max_strings);
- next_str:=xmalloc_array (str_number, max_strings);
- str_start:=xmalloc_array (pool_pointer, max_strings);
- str_pool:=xmalloc_array (pool_ASCII_code, pool_size);
-end;
-@+tini
-@z
-
-@x [46.1298] Only do get_strings_started etc. if ini.
-@!init if not get_strings_started then goto final_end;
-init_tab; {initialize the tables}
-init_prim; {call |primitive| for each primitive}
-init_str_use:=str_ptr; init_pool_ptr:=pool_ptr;@/
-max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr;
-fix_date_and_time;
-@y
-@!init if ini_version then begin
-if not get_strings_started then goto final_end;
-init_tab; {initialize the tables}
-init_prim; {call |primitive| for each primitive}
-init_str_use:=str_ptr; init_pool_ptr:=pool_ptr;@/
-max_str_ptr:=str_ptr; max_pool_ptr:=pool_ptr;
-fix_date_and_time;
-end;
-@z
-
-@x [46.1298] Set internal[prologues] in troff mode.
-history:=spotless; {ready to go!}
-@y
-history:=spotless; {ready to go!}
-if troff_mode then begin
- internal[gtroffmode]:=unity;
- internal[prologues]:=unity;
-end;
-@z
-
-@x [46.1298] Call do_final_end.
-end_of_MP: close_files_and_terminate;
-final_end: ready_already:=0;
-@y
-close_files_and_terminate;
-final_end: do_final_end;
-@z
-
-@x [46.1299] Print new line before termination; maybe switch to editor.
- print(log_name); print_char(".");
- end;
- end;
-@y
- print_file_name(0,log_name,0); print_char(".");
- end;
- end;
-print_ln;
-if (edit_name_start<>0) and (interaction>batch_mode) then
- call_edit(str_pool,edit_name_start,edit_name_length,edit_line);
-@z
-
-@x [46.1304] (final_cleanup) Only do dump if ini.
- begin @!init store_mem_file; return;@+tini@/
-@y
- begin @!init if ini_version then begin store_mem_file; return;end;@+tini@/
-@z
-
-@x [46.1306] l.22937 - Handle %&mem line
-if (mem_ident=0)or(buffer[loc]="&") then
-@y
-if (mem_ident=0)or(buffer[loc]="&")or dump_line then
-@z
-
-@x [46.1307] l.23004
-sys_random_seed := (internal[time] div unity)+internal[day];@/
-@y
-sys_random_seed := get_random_seed;@/
-@z
-
-@x [47.1307] Change read of integer.
- 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);
-@y
- m:=input_int (stdin);
- 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 n:=input_int (stdin);
-@z
-
-@x [47.1308]
-13: begin read(term_in,l); print_cmd_mod(n,l);
-@y
-13: begin l:=input_int (stdin); print_cmd_mod(n,l);
-@z
-
-@x [48.1309] Add editor-switch variable to globals.
-This section should be replaced, if necessary, by any special
-modification of the program
-that are necessary to make \MP\ 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
-Here are the variables used to hold ``switch-to-editor'' information.
-@^system dependencies@>
-
-@<Global...@>=
-@!edit_name_start: pool_pointer;
-@!edit_name_length,@!edit_line: integer;
-@!xprn: array[ASCII_code] of ASCII_code; {use \.{\^\^} notation?}
-@!stop_at_space: boolean; {whether |more_name| returns false for space}
-
-@ The |edit_name_start| will be set to point into |str_pool| somewhere after
-its beginning if \MP\ is supposed to switch to an editor on exit.
-
-@<Set init...@>=
-edit_name_start:=0;
-stop_at_space:=true;
-
-@ Dumping the |xord|, |xchr|, and |xprn| arrays. We dump these always
-in the format, so a TCX file loaded during format creation can set a
-default for users of the format.
-
-@<Dump |xord|, |xchr|, and |xprn|@>=
-dump_things(xord[0], 256);
-dump_things(xchr[0], 256);
-dump_things(xprn[0], 256);
-
-@ Undumping the |xord|, |xchr|, and |xprn| arrays. This code is more
-complicated, because we want to ensure that a TCX file specified on
-the command line will override whatever is in the format. Since the
-tcx file has already been loaded, that implies throwing away the data
-in the format. Also, if no |translate_filename| is given, but
-|eight_bit_p| is set we have to make all characters printable.
-
-@<Undump |xord|, |xchr|, and |xprn|@>=
-if translate_filename then begin
- for k:=0 to 255 do undump_things(dummy_xord, 1);
- for k:=0 to 255 do undump_things(dummy_xchr, 1);
- for k:=0 to 255 do undump_things(dummy_xprn, 1);
- end
-else begin
- undump_things(xord[0], 256);
- undump_things(xchr[0], 256);
- undump_things(xprn[0], 256);
- if eight_bit_p then
- for k:=0 to 255 do
- xprn[k]:=1;
-end;
-
-@ Web2c is deficient, and can only translate pointers to a type
-identifier, not a general type. Easier, if more annoying, to introduce
-this extra definition than to fix Web2c.
-
-@<Types...@> =
-@!str_ref_type = 0..max_str_ref;
-@z
diff --git a/Build/source/texk/web2c/mp.web b/Build/source/texk/web2c/mp.web
deleted file mode 100644
index 4b3f0491313..00000000000
--- a/Build/source/texk/web2c/mp.web
+++ /dev/null
@@ -1,24680 +0,0 @@
-% $Id: mp.web,v 1.8 2005/08/24 10:54:02 taco Exp $
-% MetaPost, by John Hobby. Public domain.
-
-% Much of this program was copied with permission from MF.web Version 1.9
-% It interprets a language very similar to D.E. Knuth's METAFONT, but with
-% changes designed to make it more suitable for PostScript output.
-
-% TeX is a trademark of the American Mathematical Society.
-% METAFONT is a trademark of Addison-Wesley Publishing Company.
-% PostScript is a trademark of Adobe Systems Incorporated.
-
-% 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}
-\def\PASCAL{Pascal}
-\def\ps{PostScript}
-\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
-\def\MF{{\tenlogo META}\-{\tenlogo FONT}}
-\def\MP{{\tenlogo META}\-{\tenlogo POST}}
-\def\<#1>{$\langle#1\rangle$}
-\def\section{\mathhexbox278}
-\let\swap=\leftrightarrow
-\def\round{\mathop{\rm round}\nolimits}
-\mathchardef\vb="026A % synonym for `\|'
-
-\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{MetaPost}
-\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 \MP, a graphics-language processor based on D. E. Knuth's \MF.
-The \PASCAL\ program that follows defines a standard version
-@:PASCAL}{\PASCAL@>
-of \MP\ 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 \MP\
-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 \MP\ 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
-\MP\ language itself, since the reader is supposed to be familiar with
-{\sl The {\logos METAFONT\/}book} as well as the manual
-@.WEB@>
-@:METAFONTbook}{\sl The {\logos METAFONT\/}book@>
-{\sl A User's Manual for MetaPost}, Computing Science Technical Report 162,
-AT\AM T Bell Laboratories.
-
-@ The present implementation is a preliminary version, but the possibilities
-for new features are limited by the desire to remain as nearly compatible
-with \MF\ as possible.
-
-On the other hand, the \.{WEB} description can be extended without changing
-the core of the program, and it has been designed so that such
-extensions are not extremely difficult to make.
-The |banner| string defined here should be changed whenever \MP\
-undergoes any modifications, so that it will be clear which version of
-\MP\ might be the guilty party when a problem arises.
-@^extensions to \MP@>
-@^system dependencies@>
-
-@d banner=='This is MetaPost, Version 1.005' {printed when \MP\ starts}
-@d metapost_version=="1.005"
-
-@ Different \PASCAL s have slightly different conventions, and the present
-@!@:PASCAL H}{\ph@>
-program is expressed in a version of \PASCAL\ that D. E. Knuth used for \MF.
-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 \MP\ program below is intended to be adaptable, without
-extensive changes, to most other versions of \PASCAL\ and commonly used
-\PASCAL-to-C translators, so it does not fully
-@!@:C@>
-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 |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 \MP\ 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 MP; {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 \MP\ 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 \MP'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_MP=1 {go here when \MP's variables are initialized}
-@d end_of_MP=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_MP@t\hskip-2pt@>, end_of_MP@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 \MP\ is being installed or
-when system wizards are fooling around with \MP\ 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 \MP's memory usage.
-@^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 \.{INIMP}, which does the extra calculations needed to
-@.INIMP@>
-initialize \MP'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 \MP\ 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 \MP\ 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 \MP\ 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 \MP's capacity. They may have different values in \.{INIMP} and
-in production versions of \MP.
-@.INIMP@>
-@^system dependencies@>
-
-@<Constants...@>=
-@!mem_max=30000; {greatest index in \MP's internal |mem| array;
- must be strictly less than |max_halfword|;
- must be equal to |mem_top| in \.{INIMP}, 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}
-@!emergency_line_length=255;
- {\ps\ output lines can be this long in unusual circumstances}
-@!stack_size=30; {maximum number of simultaneous input sources}
-@!max_read_files=4; {maximum number of simultaneously open \&{readfrom} files}
-@!max_strings=2500; {maximum number of strings; must not exceed |max_halfword|}
-@!string_vacancies=9000; {the minimum number of characters that should be
- available for the user's identifier names and strings,
- after \MP's own error messages are stored}
-@!strings_vacant=1000; {the minimum number of strings that should be available}
-@!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 \MP's own strings, which is currently about 22000}
-@!font_max=50; {maximum font number for included text fonts}
-@!font_mem_size=10000; {number of words for \.{TFM} information for text fonts}
-@!file_name_size=40; {file names shouldn't be longer than this}
-@!pool_name='MPlib:MP.POOL ';
- {string of length |file_name_size|; tells where the string pool appears}
-@.MPlib@>
-@!ps_tab_name='MPlib:PSFONTS.MAP ';
- {string of length |file_name_size|; locates font name translation table}
-@!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 \MP's capacity. But if they are changed,
-it is necessary to rerun the initialization program \.{INIMP}
-@.INIMP@>
-to generate new tables for the production \MP\ 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 \.{INIMP};
- 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}
-@d max_write_files=4 {maximum number of simultaneously open \&{write} files}
-@^system dependencies@>
-
-@ In case somebody has inadvertently made bad settings of the ``constants,''
-\MP\ checks them using a global variable called |bad|.
-
-This is the first of many sections of \MP\ 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 emergency_line_length<max_print_line 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 found3=43 {like |found|, when there's more than three 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 \MP\ 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 \MP\ only with respect to
-the \&{char} and \&{ASCII} operations, and the comparison of strings.
-
-@ Characters of text that have been converted to \MP'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 \MP\ 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 \MP\ 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 \MP\ 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 \MP\ 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 \MP\ 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 \MP\ are allowed to have in their input files.
-Appropriate changes to \MP's |char_class| table should then be made.
-(Unlike \TeX, each installation of \MP\ 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.
-
-\MP\ 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 mem 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
-\MP; 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 \MP\ 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 original version of \MF\ was prepared
-extends 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
-\MP\ to undertake appropriate corrective action.
-@:PASCAL H}{\ph@>
-@^system dependencies@>
-
-\MP'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.
-
-@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.
-\MP'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 \MP\
-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 \MP\ 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 \MP\ 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 `\.{mpout.log}'. (The transcript file
-will not contain error messages generated by the first line before the
-first \.{input} command.)
-
-The first line is even more special if we are lucky enough to have an operating
-system that treats \MP\ differently from a run-of-the-mill \PASCAL\ object
-program. It's nice to let the user start running a \MP\ job by typing
-a command line like `\.{MP cmr10}'; in such a case, \MP\ 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 \MP.
-
-The first line is special also because it may be read before \MP\ has
-input a mem 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 mem_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);
-@:MetaPost 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 \MP\ 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, \MP\ does all of its string processing by homegrown methods.
-
-\MP\ uses strings more extensively than \MF\ does, but the necessary
-operations can still be handled with a fairly 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[ss]| where |ss=next_str[s]|. The string pool
-is allocated sequentially and |str_pool[pool_ptr]| is the next unused
-location. The first string number not currently in use is |str_ptr|
-and |next_str[str_ptr]| begins a list of free string numbers. String
-pool entries |str_start[str_ptr]| up to |pool_ptr| are reserved for a
-string currently being constructed.
-
-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 \MP\ 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 via macros that can easily be redefined.
-When accessing character dimensions for the \&{infont} operator, an explicit
-offset is used to convert from |pool_ASCII_code| to |ASCII_code|.
-@^system dependencies@>
-
-@d si(#) == # {convert from |ASCII_code| to |pool_ASCII_code|}
-@d so(#) == # {convert from |pool_ASCII_code| to |ASCII_code|}
-@d min_pool_ASCII=0 {added to an |ASCII_code| to make a |pool_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|}
-@!pool_ASCII_code = 0..255; {elements of |str_pool| array}
-
-@ @<Glob...@>=
-@!str_pool:packed array[pool_pointer] of pool_ASCII_code; {the characters}
-@!str_start : array[str_number] of pool_pointer; {the starting pointers}
-@!next_str : array[str_number] of str_number; {for linking strings in order}
-@!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_use : str_number; {the initial number of strings in use}
-@!max_pool_ptr : pool_pointer; {the maximum so far of |pool_ptr|}
-@!max_str_ptr : str_number; {the maximum so far of |str_ptr|}
-
-@ Except for |strs_used_up|, the following string statistics are only
-maintained when code between |stat| $\ldots$ |tats| delimiters is not
-commented out:
-
-@<Glob...@>=
-@!strs_used_up:integer; {strings in use or unused but not reclaimed}
-@!pool_in_use:integer; {total number of cells of |str_pool| actually in use}
-@!strs_in_use:integer; {total number of strings actually in use}
-@!max_pl_used:integer; {maximum |pool_in_use| so far}
-@!max_strs_used:integer; {maximum |strs_in_use| so far}
-
-@ 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 str_stop(#)==str_start[next_str[#]] {one cell past the end of string
- number \#}
-@d length(#)==(str_stop(#)-str_start[#]) {the number of characters in string \#}
-
-@ The length of the current string is called |cur_length|. If we decide that
-the current string is not needed, |flush_cur_string| resets |pool_ptr| so that
-|cur_length| becomes zero.
-
-@d cur_length == (pool_ptr - str_start[str_ptr])
-@d flush_cur_string == 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 tries to make sure there is enough room
-by compacting the string pool if necessary. If this does not work,
-|do_compaction| aborts \MP\ and gives an apologetic error message.
-
-@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
- if pool_ptr+# > pool_size then do_compaction(#)
- else max_pool_ptr:=pool_ptr+#;
- end
-
-@ The following routine is similar to |str_room(1)| but it uses the
-argument |pool_size| to prevent |do_compaction| from aborting when
-string space is exhausted.
-
-@<Declare the procedure called |unit_str_room|@>=
-procedure unit_str_room;
-begin if pool_ptr>=pool_size then do_compaction(pool_size);
-if pool_ptr>=max_pool_ptr then max_pool_ptr:=pool_ptr+1;
-end;
-
-@ \MP'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.
-Space is eventually reclaimed by a procedure called |do_compaction| with
-the aid of a simple system system of reference counts.
-@^reference counts@>
-
-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 stat pool_in_use:=pool_in_use-length(s);
- decr(strs_in_use);
- tats@;
-if next_str[s]<>str_ptr then str_ref[s]:=0
-else begin str_ptr:=s;
- decr(strs_used_up);
- end;
-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.
-
-When getting the next unused string number from the linked list, we pretend
-that
-$$ \hbox{|max_str_ptr+1|, |max_str_ptr+2|, $\ldots$, |max_strings|} $$
-are linked sequentially even though the |next_str| entries have not been
-initialized yet. We never allow |str_ptr| to reach |max_strings|;
-|do_compaction| is responsible for making sure of this.
-
-@p @t\4@>@<Declare the procedure called |do_compaction|@>@;
-@t\4@>@<Declare the procedure called |unit_str_room|@>@;
-function make_string : str_number; {current string enters the pool}
-label restart;
-var @!s:str_number; {the new string}
-begin restart: s:=str_ptr;
-str_ptr:=next_str[s];
-if str_ptr>max_str_ptr then
- if str_ptr=max_strings then
- begin str_ptr:=s;
- do_compaction(0);
- goto restart;
- end
- else begin debug if strs_used_up<>max_str_ptr then confusion("s");@+gubed@/
-@:this can't happen s}{\quad \.s@>
- max_str_ptr:=str_ptr;
- next_str[str_ptr]:=max_str_ptr+1;
- end;
-str_ref[s]:=1;
-str_start[str_ptr]:=pool_ptr;
-incr(strs_used_up);
-stat incr(strs_in_use);
- pool_in_use:=pool_in_use+length(s);
- if pool_in_use>max_pl_used then max_pl_used:=pool_in_use;
- if strs_in_use>max_strs_used then max_strs_used:=strs_in_use;
-tats@;
-make_string:=s;
-end;
-
-@ On rare occasions, we might decide after calling |make_string| that some
-characters should be removed from the end of the last string and transferred
-to the beginning of a string under construction. This basically a matter of
-resetting |str_start[str_ptr]|. It is not practical to ensure that the new
-value for this pointer is in range, so this procedure should be used carefully.
-
-@p procedure chop_last_string(@!p:pool_pointer);
-begin stat pool_in_use:=pool_in_use-(str_start[str_ptr]-p); @+tats;
-str_start[str_ptr]:=p;
-end;
-
-@ The most interesting string operation is string pool compaction. The idea
-is to recover unused space in the |str_pool| array by recopying the strings
-to close the gaps created when some strings become unused. All string
-numbers~$k$ where |str_ref[k]=0| are to be linked into the list of free string
-numbers after |str_ptr|. If this fails to free enough pool space we issue an
-|overflow| error unless |needed=pool_size|. Calling |do_compaction|
-with |needed=pool_size| supresses all overflow tests.
-
-The compaction process starts with |last_fixed_str| because all lower numbered
-strings are permanently allocated with |max_str_ref| in their |str_ref| entries.
-
-@<Glob...@>=
-@!last_fixed_str:str_number; {last permanently allocated string}
-@!fixed_str_use:str_number; {number of permanently allocated strings}
-
-@ @<Declare the procedure called |do_compaction|@>=
-procedure do_compaction(@!needed:pool_pointer);
-label done;
-var @!str_use:str_number; {a count of strings in use}
-@!r,@!s,@!t:str_number; {strings being manipulated}
-@!p,@!q:pool_pointer; {destination and source for copying string characters}
-begin @<Advance |last_fixed_str| as far as possible and set |str_use|@>;
-r:=last_fixed_str;
-s:=next_str[r];
-p:=str_start[s];
-while s<>str_ptr do
- begin while str_ref[s]=0 do
- @<Advance |s| and add the old |s| to the list of free string numbers;
- then |goto done| if |s=str_ptr|@>;
- r:=s; s:=next_str[s];
- incr(str_use);
- @<Move string |r| back so that |str_start[r]=p|; make |p| the location
- after the end of the string@>;
- end;
-done: @<Move the current string back so that it starts at |p|@>;
-if needed<pool_size then
- @<Make sure that there is room for another string with |needed| characters@>;
-stat @<Account for the compaction and make sure the statistics agree with the
- global versions@>;
-tats@;
-strs_used_up:=str_use;
-end;
-
-@ @<Advance |last_fixed_str| as far as possible and set |str_use|@>=
-t:=next_str[last_fixed_str];
-while (str_ref[t]=max_str_ref)and(t<>str_ptr) do
- begin incr(fixed_str_use);
- last_fixed_str:=t;
- t:=next_str[t];
- end;
-str_use:=fixed_str_use
-
-@ Because of the way |flush_string| has been written, it should never be
-necessary to |goto done| here. The extra line of code seems worthwhile to
-preserve the generality of |do_compaction|.
-
-@<Advance |s| and add the old |s| to the list of free string numbers;...@>=
-begin t:=s;
-s:=next_str[s];
-next_str[r]:=s;
-next_str[t]:=next_str[str_ptr];
-next_str[str_ptr]:=t;
-if s=str_ptr then goto done;
-end
-
-@ The string currently starts at |str_start[r]| and ends just before
-|str_start[s]|. We don't change |str_start[s]| because it might be needed
-to locate the next string.
-
-@<Move string |r| back so that |str_start[r]=p|; make |p| the location...@>=
-q:=str_start[r];
-str_start[r]:=p;
-while q<str_start[s] do
- begin str_pool[p]:=str_pool[q];
- incr(p); incr(q);
- end
-
-@ Pointers |str_start[str_ptr]| and |pool_ptr| have not been updated. When
-we do this, anything between them should be moved.
-
-@ @<Move the current string back so that it starts at |p|@>=
-q:=str_start[str_ptr];
-str_start[str_ptr]:=p;
-while q<pool_ptr do
- begin str_pool[p]:=str_pool[q];
- incr(p); incr(q);
- end;
-pool_ptr:=p
-
-@ We must remember that |str_ptr| is not allowed to reach |max_strings|.
-
-@<Make sure that there is room for another string with |needed| char...@>=
-begin if str_use>=max_strings-1 then
- begin str_overflowed:=true;
- overflow("number of strings", max_strings-1-init_str_use);
-@:MetaPost capacity exceeded number of strings}{\quad number of strings@>
- end;
-if pool_ptr+needed>max_pool_ptr then
- if pool_ptr+needed>pool_size then
- begin str_overflowed:=true;
- overflow("pool size", pool_size-init_pool_ptr);
-@:MetaPost capacity exceeded pool size}{\quad pool size@>
- end
- else max_pool_ptr:=pool_ptr+needed;
-end
-
-@ Routines that can be called after string overflow need a way of checking
-whether it is safe to use |str_room|, |make_string|, or |do_compaction|.
-
-@<Glob...@>=
-@!str_overflowed:boolean; {is \MP\ aborting due to pool size of number of
- strings?}
-
-@ @<Account for the compaction and make sure the statistics agree with...@>=
-if (str_start[str_ptr]<>pool_in_use)or(str_use<>strs_in_use) then
- confusion("string");
-@:this can't happen string}{\quad string@>
-incr(pact_count);
-pact_chars:=pact_chars+pool_ptr-str_stop(last_fixed_str);
-pact_strs:=pact_strs+str_use-fixed_str_use;
-debug s:=str_ptr; t:=str_use;
- while s<=max_str_ptr do
- begin if t>max_str_ptr then confusion("""");
- incr(t); s:=next_str[s];
- end;
- if t<=max_str_ptr then confusion("""");
-gubed
-
-@ A few more global variables are needed to keep track of statistics when
-|stat| $\ldots$ |tats| blocks are not commented out.
-
-@<Glob...@>=
-@!pact_count:integer; {number of string pool compactions so far}
-@!pact_chars:integer; {total number of characters moved during compactions}
-@!pact_strs:integer; {total number of strings moved during compactions}
-
-@ @<Initialize compaction statistics@>=
-pact_count:=0;
-pact_chars:=0;
-pact_strs:=0@;
-
-@ 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_stop(s) 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 \.{INIMP} program, based in part
-on the information that \.{WEB} has output while processing \MP.
-@.INIMP@>
-@^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:0..255; {small indices or counters}
-@!g:str_number; {garbage}
-begin pool_ptr:=0; str_ptr:=0; max_pool_ptr:=0; max_str_ptr:=0;
-str_start[0]:=0;
-next_str[0]:=1;
-str_overflowed:=false;
-stat pool_in_use:=0; strs_in_use:=0;
- max_pl_used:=0; max_strs_used:=0;
- @<Initialize compaction statistics@>;
-tats@;
-strs_used_up:=0;
-@<Make the first 256 strings@>;
-@<Read the other strings from the \.{MP.POOL} file and return |true|,
- or give an error message and return |false|@>;
-last_fixed_str:=str_ptr-1;
-fixed_str_use:=str_ptr;
-exit:end;
-tini
-
-@ The first 256 strings will consist of a single character only.
-
-@<Make the first 256...@>=
-for k:=0 to 255 do
- begin 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 printed as 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 \MP\ 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, @'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 \.{MP.WEB}
-description that you are now reading, it outputs the \PASCAL\ program
-\.{MP.PAS} and also a string pool file called \.{MP.POOL}. The \.{INIMP}
-@.WEB@>@.INIMP@>
-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
-\MP's string memory.
-
-@<Glob...@>=
-@!init @!pool_file:alpha_file; {the string-pool file output by \.{TANGLE}}
-tini
-
-@ @<Read the other strings...@>=
- g := loadpoolstrings((pool_size-string_vacancies));
- if g=0 then begin
- wake_up_terminal; write_ln(term_out,'! You have to increase POOLSIZE.');
- get_strings_started:=false;
- return;
- end;
- get_strings_started:=true;
-
-
-@ The \.{WEB} operation \.{@@\$} denotes the value that should be at the
-end of this \.{MP.POOL} file; any other value means that the wrong pool
-file has been loaded.
-@^check sum@>
-
-
-@* \[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 |ps_file_only| prints only on the \ps\ output file.
-
-\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..max_write_files-1| prints on one of the files used for the \&{write}
-@:write_}{\&{write} primitive@>
- command.
-
-\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|. These
-relations are not used when |selector| could be |pseudo|, |new_string|,
-or |ps_file_only|. We need not check for unprintable characters when
-|selector<pseudo|.
-
-Four additional global variables, |tally|, |term_offset|, |file_offset|,
-and |ps_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|,
-|file_offset|, and |ps_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, the transcript file, or the \ps\ output file, respectively.
-
-@d new_string=max_write_files {printing is deflected to the string pool}
-@d ps_file_only=new_string+1 {printing goes to the \ps\ output file}
-@d pseudo=new_string+2 {special |selector| setting for |show_context|}
-@d no_print=new_string+3 {|selector| setting that makes data disappear}
-@d term_only=new_string+4 {printing is destined for the terminal only}
-@d log_only=new_string+5 {printing is destined for the transcript file only}
-@d term_and_log=new_string+6 {normal |selector| setting}
-@d max_selector=term_and_log {highest selector setting}
-
-@<Glob...@>=
-@!log_file : alpha_file; {transcript of \MP\ session}
-@!ps_file: alpha_file; {the generic font output goes here}
-@!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}
-@!ps_offset : integer;
- {the number of characters on the current \ps\ 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; ps_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)
-@d wps(#)==write(ps_file,#)
-@d wps_ln(#)==write_ln(ps_file,#)
-@d wps_cr==write_ln(ps_file)
-
-@ To end a line of text output, we call |print_ln|. Cases |0..max_write_files|
-use an array |wr_file| that will be declared later.
-
-@<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;
-ps_file_only: begin wps_cr; ps_offset:=0;
- end;
-no_print,pseudo,new_string: do_nothing;
-othercases write_ln(wr_file[selector])
-endcases;
-end; {note that |tally| is not affected}
-
-@ The |print_visible_char| procedure sends one character to the desired
-destination, using the |xchr| array to map it into an external character
-compatible with |input_ln|. (It assumes that it is always called with
-a visible ASCII character.) All printing comes through |print_ln| or
-|print_char|, which ultimately calls |print_visible_char|, hence these
-routines are the ones that limit lines to at most |max_print_line| characters.
-But we must make an exception for the \ps\ output file since it is not safe
-to cut up lines arbitrarily in \ps.
-
-Procedure |unit_str_room| needs to be declared |forward| here because it calls
-|do_compaction| and |do_compaction| can call the error routines. Actually,
-|unit_str_room| avoids |overflow| errors but it can call |confusion|.
-
-@<Basic printing...@>=
-procedure@?unit_str_room; forward;@t\2@>@/
-procedure print_visible_char(@!s:ASCII_code); {prints a single character}
-label done;
-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;
-ps_file_only: if s=13 then begin
- wps_cr; ps_offset:=0;
- end
- else
- begin
- wps(xchr[s]); incr(ps_offset);
- end;
-no_print: do_nothing;
-pseudo: if tally<trick_count then trick_buf[tally mod error_line]:=s;
-new_string: begin if pool_ptr>=max_pool_ptr then
- begin unit_str_room;
- if pool_ptr>=pool_size then goto done;
- {drop characters if string space is full}
- end;
- append_char(s);
- end;
-othercases write(wr_file[selector],xchr[s])
-endcases;
-done:incr(tally);
-end;
-
-@ The |print_char| procedure sends one character to the desired destination.
-File names and string expressions might contain |ASCII_code| values that
-can't be printed using |print_visible_char|. These characters will be
-printed in three- or four-symbol form like `\.{\^\^A}' or `\.{\^\^e4}'.
-(This procedure assumes that it is safe to bypass all checks for unprintable
-characters when |selector| is in the range |0..max_write_files-1| or when
-|selector=ps_file_only|. In the former case the user might want to write
-unprintable characters, and in the latter case the \ps\ printing routines
-check their arguments themselves before calling |print_char| or |print|.)
-
-@d print_lc_hex(#)==l:=#;
- if l<10 then print_visible_char(l+"0")@+else print_visible_char(l-10+"a")
-
-@<Basic printing...@>=
-procedure print_char(@!k:ASCII_code); {prints a single character}
-var l:0..255; {small index or counter}
-begin if selector<pseudo then print_visible_char(k)
-else if @<Character |k| cannot be printed@> then
- begin print_visible_char("^"); print_visible_char("^");
- if k<@'100 then print_visible_char(k+@'100)
- else if k<@'200 then print_visible_char(k-@'100)
- else begin print_lc_hex(k div 16); print_lc_hex(k mod 16);
- end;
- end
-else print_visible_char(k);
-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 \MP\ 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>max_str_ptr) then s:="???"; {this can't happen}
-@.???@>
-j:=str_start[s];
-while j<str_stop(s) do
- begin print_char(so(str_pool[j])); incr(j);
- end;
-end;
-
-@ By popular demand, \MP\ prints the banner line only on the transcript file.
-Thus there is nothing special to be printed here.
-
-@<Initialize the output...@>=
-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 case selector of
-term_and_log: if (term_offset>0)or(file_offset>0) then print_ln;
-log_only: if file_offset>0 then print_ln;
-term_only: if term_offset>0 then print_ln;
-ps_file_only: if ps_offset>0 then print_ln;
-no_print,pseudo,new_string: do_nothing;
-end; {there are no other cases}
-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;
-
-@ \MP\ 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, \MP\ 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;
-
-@ \MP\ 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, \MP\ 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 \MP\ survives the initialization process.
-
-@<Set init...@>=
-deletions_allowed:=true; error_count:=0; {|history| is initialized elsewhere}
-
-@ Since errors can be detected almost anywhere in \MP, 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 \MP\ 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}}
-@!filename_template:str_number; {a string set up by \&{filenametemplate}}
-
-@ @<Set init...@>=
-help_ptr:=0; use_err_help:=false; err_help:=0; filename_template:=0;
-
-@ The |jump_out| procedure just cuts across all active procedure levels and
-goes to |end_of_MP|. This is the only nonlocal |@!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_MP;
-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 \MP\ 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@>
- print(input_stack[file_ptr].name_field);
- print(" at line "); print_int(true_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 \MP\ 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 \MP'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_stop(err_help) do
- begin if str_pool[j]<>si("%") then print(so(str_pool[j]))
- else if j+1=str_stop(err_help) 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 \MP'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("MetaPost capacity exceeded, sorry [");
-@.MetaPost 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 \MP\
-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 \MP\ 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 signaled. 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 \MP\ 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. \MP\ 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 \MP'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 \MP\ 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 \MP 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 \MP\ in a similar way.
-
-@d el_gordo == @'17777777777 {$2^{31}-1$, the largest value that \MP\ likes}
-
-@ One of \MP'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 \MP\ is being implemented with languages that permit
-binary shifting, the |half| macro should be changed to make this operation
-as efficient as possible. Since some languages have shift operators that can
-only be trusted to work on positive numbers, there is also a macro |halfp|
-that is used only when the quantity being halved is known to be positive
-or zero.
-
-@d half(#)==(#) div 2
-@d halfp(#)==(#) 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:=halfp(a+1);
-end;
-
-@ Conversely, here is a procedure analogous to |print_int|. If the output
-of this procedure is subsequently read by \MP\ 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 \MP\ programs are generally supposed to be
-less than $2^{12}$ in absolute value, so \MP\ 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; \MP\ 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 \MP's internal computations have
-been designed to avoid this sort of error.
-
-If this subroutine were programmed in assembly language on a typical
-machine, we could simply compute |(@t$2^{28}$@>*p)div q|, since a
-double-precision product can often be input to a fixed-point division
-instruction. But when we are restricted to \PASCAL\ arithmetic it
-is necessary either to resort to multiple-precision maneuvering
-or to 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 \MP'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 \MP\ 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 \MP's computation
-time during typical jobs, so a machine-language 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:=halfp(p+q)@+else p:=halfp(p);
- f:=halfp(f);
- until f=1
-else repeat if odd(f) then p:=p+halfp(q-p)@+else p:=halfp(p);
- f:=halfp(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 a machine-language replacement 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:=halfp(p+q)@+else p:=halfp(p);
- f:=halfp(f);
- until f=1
-else repeat if odd(f) then p:=p+halfp(q-p)@+else p:=halfp(p);
- f:=halfp(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 \MP'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 \MP'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, \MP\ 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 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.
-\MP\ 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:=halfp(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:=halfp(a); b:=halfp(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:=halfp(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;
-othercases n_arg:=-z; { |eighth_octant| }
-end
-
-@ 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:=halfp(x); y:=halfp(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.
-\MP\ 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-31})\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.
-The global variable |sys_random_seed| was introduced in version 0.9,
-for the sole reason of stressing the fact that the initial value of the
-random seed is system-dependant. The pascal code below will initialize
-this variable to |(internal[time] div unity)+internal[day]|, but this is
-not good enough on modern fast machines that are capable of running
-multiple MetaPost processes within the same second.
-@^system dependencies@>
-
-@<Glob...@>=
-@!randoms:array[0..54] of fraction; {the last 55 random values generated}
-@!j_random:0..54; {the number of unused |randoms|}
-@!sys_random_seed:scaled; {the default random seed}
-
-@ To consume a random fraction, the program below will say `|next_random|'
-and then it will fetch |randoms[j_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:=halfp(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, \MP\ 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. \MP\ 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 \MP\ 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:=8;@+tini@;@/
-if mem_max<mem_top then bad:=8;
-if (min_quarterword>0)or(max_quarterword<127) then bad:=9;
-if (min_halfword>0)or(max_halfword<32767) then bad:=10;
-if (min_quarterword<min_halfword)or@|
- (max_quarterword>max_halfword) then bad:=11;
-if (mem_min<min_halfword)or(mem_max>=max_halfword) then bad:=12;
-if max_strings>max_halfword then bad:=13;
-if buf_size>max_halfword then bad:=14;
-if font_max>max_halfword then bad:=15;
-if (max_quarterword-min_quarterword<255)or@|
- (max_halfword-min_halfword<65535) then bad:=16;
-
-@ The operation of subtracting |min_halfword| occurs rather frequently in
-\MP, so it is convenient to abbreviate this operation by using the macro
-|ho| defined here. \MP\ 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 \MP\ 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 \MP\ are handled by providing a large array |mem| in
-which consecutive blocks of words are used as nodes by the \MP\ 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 mem files, by the \.{INIMP} preprocessor.
-@.INIMP@>
-Production versions of \MP\ 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 particular applications can
-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,'' \MP\ 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, \MP\ 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}
-@:MetaPost 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}
-@:MetaPost 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 \MP\ 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 \.{INIMP} 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+1| are always used to store a
-two-word dummy token whose second word is zero.
-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_dash==mem_min+2 {the first two words are reserved for a null value}
-@d dep_head==null_dash+3 {we will define |dash_node_size=3|}
-@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 test_pen==inf_val+2
- {nine words for a pen used when testing the turning number}
-@d bad_vardef==test_pen+9 {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 spec_head==mem_top-3 {head of a list of unprocessed \&{special} items}
-@d hi_mem_stat_min==mem_top-3 {smallest statically allocated word in
- the one-word |mem|}
-
-@ The following code gets the dynamic part of |mem| off to a good start,
-when \MP\ is initializing itself the slow way.
-
-@<Initialize table entries (done by \.{INIMP} 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_stat_min);
- {initialize statistics}
-@<Initialize a pen at |test_pen| so that it fits in nine words@>;
-
-@ 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 \MP\ 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 \MP'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,done3; {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 @<Do intialization required before printing new busy locations@>;
-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
- @<Indicate that |p| is a new busy location@>;
-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
- @<Indicate that |p| is a new busy location@>;
-@<Finish printing new busy locations@>;
-end
-
-@ There might be many new busy locations so we are careful to print contiguous
-blocks compactly. During this operation |q| is the last new busy location and
-|r| is the start of the block containing |q|.
-
-@<Indicate that |p| is a new busy location@>=
-begin if p>q+1 then
- begin if q>r then
- begin print(".."); print_int(q);
- end;
- print_char(" "); print_int(p);
- r:=p;
- end;
-q:=p;
-end
-
-@ @<Do intialization required before printing new busy locations@>=
-q:=mem_max; r:=mem_max
-
-@ @<Finish printing new busy locations@>=
-if q>r then
- begin print(".."); print_int(q);
- 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 \MP. 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. Anything less than |min_command| is
-eliminated during macro expansions, and anything no more than |max_pre_command|
-is eliminated when expanding \TeX\ material. Ranges such as
-|min_secondary_command..max_secondary_command| are used when parsing
-expressions, but the relative ordering within such a range is generally not
-critical.
-
-The ordering of the highest-numbered commands
-(|comma<semicolon<end_group<stop|) is crucial for the parsing and
-error-recovery methods of this program as is the ordering |if_test<fi_or_else|
-for the smallest two commands. The ordering is also important in the ranges
-|numeric_token..plus_or_minus| and |left_brace..ampersand|.
-
-At any rate, here is the list, for future reference.
-
-@d start_tex=1 {begin \TeX\ material (\&{btex}, \&{verbatimtex})}
-@d etex_marker=2 {end \TeX\ material (\&{etex})}
-@d mpx_break=3 {stop reading an \.{MPX} file (\&{mpxbreak})}
-@d max_pre_command=mpx_break
-@d if_test=4 {conditional text (\&{if})}
-@d fi_or_else=5 {delimiters for conditionals (\&{elseif}, \&{else}, \&{fi})}
-@d input=6 {input a source file (\&{input}, \&{endinput})}
-@d iteration=7 {iterate (\&{for}, \&{forsuffixes}, \&{forever}, \&{endfor})}
-@d repeat_loop=8 {special command substituted for \&{endfor}}
-@d exit_test=9 {premature exit from a loop (\&{exitif})}
-@d relax=10 {do nothing (\.{\char`\\})}
-@d scan_tokens=11 {put a string into the input buffer}
-@d expand_after=12 {look ahead one token}
-@d defined_macro=13 {a macro defined by the user}
-@d min_command=defined_macro+1
-@d save_command=14 {save a list of tokens (\&{save})}
-@d interim_command=15 {save an internal quantity (\&{interim})}
-@d let_command=16 {redefine a symbolic token (\&{let})}
-@d new_internal=17 {define a new internal quantity (\&{newinternal})}
-@d macro_def=18 {define a macro (\&{def}, \&{vardef}, etc.)}
-@d ship_out_command=19 {output a character (\&{shipout})}
-@d add_to_command=20 {add to edges (\&{addto})}
-@d bounds_command=21 {add bounding path to edges (\&{setbounds}, \&{clip})}
-@d tfm_command=22 {command for font metric info (\&{ligtable}, etc.)}
-@d protection_command=23 {set protection flag (\&{outer}, \&{inner})}
-@d show_command=24 {diagnostic output (\&{show}, \&{showvariable}, etc.)}
-@d mode_command=25 {set interaction level (\&{batchmode}, etc.)}
-@d random_seed=26 {initialize random number generator (\&{randomseed})}
-@d message_command=27 {communicate to user (\&{message}, \&{errmessage})}
-@d every_job_command=28 {designate a starting token (\&{everyjob})}
-@d delimiters=29 {define a pair of delimiters (\&{delimiters})}
-@d special_command=30 {output special info (\&{special})
- or font map info (\&{fontmapfile}, \&{fontmapline})}
-@d write_command=31 {write text to a file (\&{write})}
-@d type_name=32 {declare a type (\&{numeric}, \&{pair}, etc.)}
-@d max_statement_command=type_name
-@d min_primary_command=type_name
-@d left_delimiter=33 {the left delimiter of a matching pair}
-@d begin_group=34 {beginning of a group (\&{begingroup})}
-@d nullary=35 {an operator without arguments (e.g., \&{normaldeviate})}
-@d unary=36 {an operator with one argument (e.g., \&{sqrt})}
-@d str_op=37 {convert a suffix to a string (\&{str})}
-@d cycle=38 {close a cyclic path (\&{cycle})}
-@d primary_binary=39 {binary operation taking `\&{of}' (e.g., \&{point})}
-@d capsule_token=40 {a value that has been put into a token list}
-@d string_token=41 {a string constant (e.g., |"hello"|)}
-@d internal_quantity=42 {internal numeric parameter (e.g., \&{pausing})}
-@d min_suffix_token=internal_quantity
-@d tag_token=43 {a symbolic token without a primitive meaning}
-@d numeric_token=44 {a numeric constant (e.g., \.{3.14159})}
-@d max_suffix_token=numeric_token
-@d plus_or_minus=45 {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=46 {a macro defined by \&{secondarydef}}
-@d tertiary_binary=47 {an operator at the tertiary level (e.g., `\.{++}')}
-@d max_tertiary_command=tertiary_binary
-@d left_brace=48 {the operator `\.{\char`\{}'}
-@d min_expression_command=left_brace
-@d path_join=49 {the operator `\.{..}'}
-@d ampersand=50 {the operator `\.\&'}
-@d expression_tertiary_macro=51 {a macro defined by \&{tertiarydef}}
-@d expression_binary=52 {an operator at the expression level (e.g., `\.<')}
-@d equals=53 {the operator `\.='}
-@d max_expression_command=equals
-@d and_command=54 {the operator `\&{and}'}
-@d min_secondary_command=and_command
-@d secondary_primary_macro=55 {a macro defined by \&{primarydef}}
-@d slash=56 {the operator `\./'}
-@d secondary_binary=57 {an operator at the binary level (e.g., \&{shifted})}
-@d max_secondary_command=secondary_binary
-@d param_type=58 {type of parameter (\&{primary}, \&{expr}, \&{suffix}, etc.)}
-@d controls=59 {specify control points explicitly (\&{controls})}
-@d tension=60 {specify tension between knots (\&{tension})}
-@d at_least=61 {bounded tension value (\&{atleast})}
-@d curl_command=62 {specify curl at an end knot (\&{curl})}
-@d macro_special=63 {special macro operators (\&{quote}, \.{\#\AT!}, etc.)}
-@d right_delimiter=64 {the right delimiter of a matching pair}
-@d left_bracket=65 {the operator `\.['}
-@d right_bracket=66 {the operator `\.]'}
-@d right_brace=67 {the operator `\.{\char`\}}'}
-@d with_option=68 {option for filling (\&{withpen}, \&{withweight}, etc.)}
-@d thing_to_add=69
- {variant of \&{addto} (\&{contour}, \&{doublepath}, \&{also})}
-@d of_token=70 {the operator `\&{of}'}
-@d to_token=71 {the operator `\&{to}'}
-@d step_token=72 {the operator `\&{step}'}
-@d until_token=73 {the operator `\&{until}'}
-@d within_token=74 {the operator `\&{within}'}
-@d lig_kern_token=75
- {the operators `\&{kern}' and `\.{=:}' and `\.{=:\char'174}', etc.}
-@d assignment=76 {the operator `\.{:=}'}
-@d skip_to=77 {the operation `\&{skipto}'}
-@d bchar_label=78 {the operator `\.{\char'174\char'174:}'}
-@d double_colon=79 {the operator `\.{::}'}
-@d colon=80 {the operator `\.:'}
-@#
-@d comma=81 {the operator `\.,', must be |colon+1|}
-@d end_of_statement==cur_cmd>comma
-@d semicolon=82 {the operator `\.;', must be |comma+1|}
-@d end_group=83 {end a group (\&{endgroup}), must be |semicolon+1|}
-@d stop=84 {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 \MP\ have a variety of ``types,''
-distinguished by the code numbers defined here. These numbers are also
-not completely arbitrary. Things that get expanded must have types
-|>independent|; a type remaining after expansion is numeric if and only if
-its code number is at least |numeric_type|; objects containing numeric
-parts must have types between |transform_type| and |pair_type|;
-all other types must be smaller than |transform_type|; and among the types
-that are not unknown or vacuous, the smallest two must be |boolean_type|
-and |string_type| in that order.
-
-@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 path_type=8 {\&{path} with a known value}
-@d unknown_path=path_type+unknown_tag
-@d picture_type=10 {\&{picture} with a known value}
-@d unknown_picture=picture_type+unknown_tag
-@d transform_type=12 {\&{transform} variable or capsule}
-@d color_type=13 {\&{color} variable or capsule}
-@d cmykcolor_type=14 {\&{cmykcolor} variable or capsule}
-@d pair_type=15 {\&{pair} variable or capsule}
-@d numeric_type=16 {variable that has been declared \&{numeric} but not used}
-@d known=17 {\&{numeric} with a known value}
-@d dependent=18 {a linear combination with |fraction| coefficients}
-@d proto_dependent=19 {a linear combination with |scaled| coefficients}
-@d independent=20 {\&{numeric} with unknown value}
-@d token_list=21 {variable name or suffix argument or text argument}
-@d structured=22 {variable with subscripts and attributes}
-@d unsuffixed_macro=23 {variable defined with \&{vardef} but no \.{\AT!\#}}
-@d suffixed_macro=24 {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");
-path_type:print("path");
-unknown_path:print("unknown path");
-picture_type:print("picture");
-unknown_picture:print("unknown picture");
-transform_type:print("transform");
-color_type:print("color");
-cmykcolor_type:print("cmykcolor");
-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 \MP\ 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 red_part_sector=11 {|name_type| in the \&{redpart} of a node}
-@d green_part_sector=12 {|name_type| in the \&{greenpart} of a node}
-@d blue_part_sector=13 {|name_type| in the \&{bluepart} of a node}
-@d cyan_part_sector=14 {|name_type| in the \&{redpart} of a node}
-@d magenta_part_sector=15 {|name_type| in the \&{greenpart} of a node}
-@d yellow_part_sector=16 {|name_type| in the \&{bluepart} of a node}
-@d black_part_sector=17 {|name_type| in the \&{greenpart} of a node}
-@d grey_part_sector=18 {|name_type| in the \&{bluepart} of a node}
-@d capsule=19 {|name_type| in stashed-away subexpressions}
-@d token=20 {|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. The relative values
-are not critical, except for |true_code..false_code|, |or_op..and_op|,
-and |filled_op..bounded_op|. The restrictions are that
-|and_op-false_code=or_op-true_code|, that the ordering of
-|x_part...blue_part| must match that of |x_part_sector..blue_part_sector|,
-and the ordering of |filled_op..bounded_op| must match that of the code
-values they test for.
-
-@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 read_from_op=38 {operation code for \.{readfrom}}
-@d close_from_op=39 {operation code for \.{closefrom}}
-@d odd_op=40 {operation code for \.{odd}}
-@d known_op=41 {operation code for \.{known}}
-@d unknown_op=42 {operation code for \.{unknown}}
-@d not_op=43 {operation code for \.{not}}
-@d decimal=44 {operation code for \.{decimal}}
-@d reverse=45 {operation code for \.{reverse}}
-@d make_path_op=46 {operation code for \.{makepath}}
-@d make_pen_op=47 {operation code for \.{makepen}}
-@d oct_op=48 {operation code for \.{oct}}
-@d hex_op=49 {operation code for \.{hex}}
-@d ASCII_op=50 {operation code for \.{ASCII}}
-@d char_op=51 {operation code for \.{char}}
-@d length_op=52 {operation code for \.{length}}
-@d turning_op=53 {operation code for \.{turningnumber}}
-@d color_model_part=54 {operation code for \.{colormodel}}
-@d x_part=55 {operation code for \.{xpart}}
-@d y_part=56 {operation code for \.{ypart}}
-@d xx_part=57 {operation code for \.{xxpart}}
-@d xy_part=58 {operation code for \.{xypart}}
-@d yx_part=59 {operation code for \.{yxpart}}
-@d yy_part=60 {operation code for \.{yypart}}
-@d red_part=61 {operation code for \.{redpart}}
-@d green_part=62 {operation code for \.{greenpart}}
-@d blue_part=63 {operation code for \.{bluepart}}
-@d cyan_part=64 {operation code for \.{cyanpart}}
-@d magenta_part=65 {operation code for \.{magentapart}}
-@d yellow_part=66 {operation code for \.{yellowpart}}
-@d black_part=67 {operation code for \.{blackpart}}
-@d grey_part=68 {operation code for \.{greypart}}
-@d font_part=69 {operation code for \.{fontpart}}
-@d text_part=70 {operation code for \.{textpart}}
-@d path_part=71 {operation code for \.{pathpart}}
-@d pen_part=72 {operation code for \.{penpart}}
-@d dash_part=73 {operation code for \.{dashpart}}
-@d sqrt_op=74 {operation code for \.{sqrt}}
-@d m_exp_op=75 {operation code for \.{mexp}}
-@d m_log_op=76 {operation code for \.{mlog}}
-@d sin_d_op=77 {operation code for \.{sind}}
-@d cos_d_op=78 {operation code for \.{cosd}}
-@d floor_op=79 {operation code for \.{floor}}
-@d uniform_deviate=80 {operation code for \.{uniformdeviate}}
-@d char_exists_op=81 {operation code for \.{charexists}}
-@d font_size=82 {operation code for \.{fontsize}}
-@d ll_corner_op=83 {operation code for \.{llcorner}}
-@d lr_corner_op=84 {operation code for \.{lrcorner}}
-@d ul_corner_op=85 {operation code for \.{ulcorner}}
-@d ur_corner_op=86 {operation code for \.{urcorner}}
-@d arc_length=87 {operation code for \.{arclength}}
-@d angle_op=88 {operation code for \.{angle}}
-@d cycle_op=89 {operation code for \.{cycle}}
-@d filled_op=90 {operation code for \.{filled}}
-@d stroked_op=91 {operation code for \.{stroked}}
-@d textual_op=92 {operation code for \.{textual}}
-@d clipped_op=93 {operation code for \.{clipped}}
-@d bounded_op=94 {operation code for \.{bounded}}
-@d plus=95 {operation code for \.+}
-@d minus=96 {operation code for \.-}
-@d times=97 {operation code for \.*}
-@d over=98 {operation code for \./}
-@d pythag_add=99 {operation code for \.{++}}
-@d pythag_sub=100 {operation code for \.{+-+}}
-@d or_op=101 {operation code for \.{or}}
-@d and_op=102 {operation code for \.{and}}
-@d less_than=103 {operation code for \.<}
-@d less_or_equal=104 {operation code for \.{<=}}
-@d greater_than=105 {operation code for \.>}
-@d greater_or_equal=106 {operation code for \.{>=}}
-@d equal_to=107 {operation code for \.=}
-@d unequal_to=108 {operation code for \.{<>}}
-@d concatenate=109 {operation code for \.\&}
-@d rotated_by=110 {operation code for \.{rotated}}
-@d slanted_by=111 {operation code for \.{slanted}}
-@d scaled_by=112 {operation code for \.{scaled}}
-@d shifted_by=113 {operation code for \.{shifted}}
-@d transformed_by=114 {operation code for \.{transformed}}
-@d x_scaled=115 {operation code for \.{xscaled}}
-@d y_scaled=116 {operation code for \.{yscaled}}
-@d z_scaled=117 {operation code for \.{zscaled}}
-@d in_font=118 {operation code for \.{infont}}
-@d intersect=119 {operation code for \.{intersectiontimes}}
-@d double_dot=120 {operation code for improper \.{..}}
-@d substring_of=121 {operation code for \.{substring}}
-@d min_of=substring_of
-@d subpath_of=122 {operation code for \.{subpath}}
-@d direction_time_of=123 {operation code for \.{directiontime}}
-@d point_of=124 {operation code for \.{point}}
-@d precontrol_of=125 {operation code for \.{precontrol}}
-@d postcontrol_of=126 {operation code for \.{postcontrol}}
-@d pen_offset_of=127 {operation code for \.{penoffset}}
-@d arc_time_of=128 {operation code for \.{arctime}}
-@d mp_version=129 {operation code for \.{mpversion}}
-
-@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");
-read_from_op:print("readfrom");
-close_from_op:print("closefrom");
-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");
-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");
-red_part:print("redpart");
-green_part:print("greenpart");
-blue_part:print("bluepart");
-cyan_part:print("cyanpart");
-magenta_part:print("magentapart");
-yellow_part:print("yellowpart");
-black_part:print("blackpart");
-grey_part:print("greypart");
-color_model_part:print("colormodel");
-font_part:print("fontpart");
-text_part:print("textpart");
-path_part:print("pathpart");
-pen_part:print("penpart");
-dash_part:print("dashpart");
-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");
-font_size:print("fontsize");
-ll_corner_op:print("llcorner");
-lr_corner_op:print("lrcorner");
-ul_corner_op:print("ulcorner");
-ur_corner_op:print("urcorner");
-arc_length:print("arclength");
-angle_op:print("angle");
-cycle_op:print("cycle");
-filled_op:print("filled");
-stroked_op:print("stroked");
-textual_op:print("textual");
-clipped_op:print("clipped");
-bounded_op:print("bounded");
-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");
-in_font:print("infont");
-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");
-arc_time_of:print("arctime");
-mp_version:print("mpversion");
-othercases print("..")
-endcases;
-end;
-
-@ \MP\ 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 path subdivision prior to filling with polygonal a pen}
-@d tracing_commands=6 {show commands and operations before they are performed}
-@d tracing_restores=7 {show when a variable or internal is restored}
-@d tracing_macros=8 {show macros before they are expanded}
-@d tracing_output=9 {show digitized edges as they are output}
-@d tracing_stats=10 {show memory usage at end of job}
-@d tracing_lost_chars=11 {show characters that aren't \&{infont}}
-@d tracing_online=12 {show long diagnostics on terminal and in the log file}
-@d year=13 {the current year (e.g., 1984)}
-@d month=14 {the current month (e.g., 3 $\equiv$ March)}
-@d day=15 {the current day of the month}
-@d time=16 {the number of minutes past midnight when this job started}
-@d char_code=17 {the number of the next character to be output}
-@d char_ext=18 {the extension code of the next character to be output}
-@d char_wd=19 {the width of the next character to be output}
-@d char_ht=20 {the height of the next character to be output}
-@d char_dp=21 {the depth of the next character to be output}
-@d char_ic=22 {the italic correction of the next character to be output}
-@d design_size=23 {the unit of measure used for |char_wd..char_ic|, in points}
-@d pausing=24 {positive to display lines on the terminal before they are read}
-@d showstopping=25 {positive to stop after each \&{show} command}
-@d fontmaking=26 {positive if font metric output is to be produced}
-@d linejoin=27 {as in \ps: 0 for mitered, 1 for round, 2 for beveled}
-@d linecap=28 {as in \ps: 0 for butt, 1 for round, 2 for square}
-@d miterlimit=29 {controls miter length as in \ps}
-@d warning_check=30 {controls error message when variable value is large}
-@d boundary_char=31 {the right boundary character for ligatures}
-@d prologues=32 {positive to output conforming PostScript using built-in fonts}
-@d true_corners=33 {positive to make \&{llcorner} etc. ignore \&{setbounds}}
-@d default_color_model=34 {the default color model for unspecified items}
-@d restore_clip_color=35
-@d mpprocset=36 {wether or not create PostScript command shortcuts}
-@d gtroffmode=37 {whether the user specified |-troff| on the command line}
-@d max_given_internal=37
-
-@<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 \MP'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 \MP'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("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("tracingoutput",internal_quantity,tracing_output);@/
-@!@:tracing_output_}{\&{tracingoutput} primitive@>
-primitive("tracingstats",internal_quantity,tracing_stats);@/
-@!@:tracing_stats_}{\&{tracingstats} primitive@>
-primitive("tracinglostchars",internal_quantity,tracing_lost_chars);@/
-@!@:tracing_lost_chars_}{\&{tracinglostchars} 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("designsize",internal_quantity,design_size);@/
-@!@:design_size_}{\&{designsize} 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("linejoin",internal_quantity,linejoin);@/
-@!@:linejoin_}{\&{linejoin} primitive@>
-primitive("linecap",internal_quantity,linecap);@/
-@!@:linecap_}{\&{linecap} primitive@>
-primitive("miterlimit",internal_quantity,miterlimit);@/
-@!@:miterlimit_}{\&{miterlimit} primitive@>
-primitive("warningcheck",internal_quantity,warning_check);@/
-@!@:warning_check_}{\&{warningcheck} primitive@>
-primitive("boundarychar",internal_quantity,boundary_char);@/
-@!@:boundary_char_}{\&{boundarychar} primitive@>
-primitive("prologues",internal_quantity,prologues);@/
-@!@:prologues_}{\&{prologues} primitive@>
-primitive("truecorners",internal_quantity,true_corners);@/
-@!@:true_corners_}{\&{truecorners} primitive@>
-primitive("mpprocset",internal_quantity,mpprocset);@/
-@!@:mpprocset_}{\&{mpprocset} primitive@>
-primitive("troffmode",internal_quantity,gtroffmode);@/
-@!@:troffmode_}{\&{troffmode} primitive@>
-primitive("defaultcolormodel",internal_quantity,default_color_model);@/
-@!@:default_color_model_}{\&{defaultcolormodel} primitive@>
-primitive("restoreclipcolor",internal_quantity,restore_clip_color);@/
-@!@:restore_clip_color_}{\&{restoreclipcolor} primitive@>
-
-@ Colors can be specified in four color models. In the special
-case of |no_model|, MetaPost does not output any color operator to
-the postscript output.
-
-Note: these values are passed directly on to |with_option|. This only
-works because the other possible values passed to |with_option| are
-8 and 10 respectively (from |with_pen| and |with_picture|).
-
-There is a first state, that is only used for |gs_colormodel|. It flags
-the fact that there has not been any kind of color specification by
-the user so far in the game.
-
-@d no_model=1
-@d grey_model=3
-@d rgb_model=5
-@d cmyk_model=7
-@d uninitialized_model=9
-
-@<Initialize table entries (done by \.{INIMP} only)@>=
-internal[default_color_model]:=(rgb_model*unity);
-internal[restore_clip_color]:=unity;
-
-@ 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_commands]:="tracingcommands";
-int_name[tracing_restores]:="tracingrestores";
-int_name[tracing_macros]:="tracingmacros";
-int_name[tracing_output]:="tracingoutput";
-int_name[tracing_stats]:="tracingstats";
-int_name[tracing_lost_chars]:="tracinglostchars";
-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[design_size]:="designsize";
-int_name[pausing]:="pausing";
-int_name[showstopping]:="showstopping";
-int_name[fontmaking]:="fontmaking";
-int_name[linejoin]:="linejoin";
-int_name[linecap]:="linecap";
-int_name[miterlimit]:="miterlimit";
-int_name[warning_check]:="warningcheck";
-int_name[boundary_char]:="boundarychar";
-int_name[prologues]:="prologues";
-int_name[true_corners]:="truecorners";
-int_name[default_color_model]:="defaultcolormodel";
-int_name[mpprocset]:="mpprocset";
-int_name[gtroffmode]:="troffmode";
-int_name[restore_clip_color]:="restoreclipcolor";
-
-@ The following procedure, which is called just before \MP\ 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 \MP\ 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;
-
-@ \MP\ 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...@>=
-@<Declare a function called |true_line|@>@;
-procedure begin_diagnostic; {prepare to do some tracing}
-begin old_setting:=selector;
-if selector=ps_file_only then selector:=non_ps_setting;
-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;
-
-@ The global variable |non_ps_setting| is initialized when it is time to print
-on |ps_file|.
-
-@<Glob...@>=
-@!old_setting,@!non_ps_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(true_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 \MP.
-
-@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_etex==hash_top+11 {|hash| location of a permanent \&{etex}}
-@d frozen_mpx_break==hash_top+12 {|hash| location of a permanent \&{mpxbreak}}
-@d frozen_bad_vardef==hash_top+13 {|hash| location of `\.{a bad variable}'}
-@d frozen_undefined==hash_top+14 {|hash| location that never gets defined}
-@d hash_end==hash_top+14 {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_etex):="etex";
-text(frozen_mpx_break):="mpxbreak";
-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:=17;
-
-@ 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);
-@:MetaPost 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 \MP'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 \MP\ 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_stop(s)-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(text(cur_sym)); text(cur_sym):=s;
- end;
-eq_type(cur_sym):=c; equiv(cur_sym):=o;
-end;
-tini
-
-@ Many of \MP'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 \MP'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("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("curl",curl_command,0);@/
-@!@:curl_}{\&{curl} primitive@>
-primitive("delimiters",delimiters,0);@/
-@!@:delimiters_}{\&{delimiters} 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("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("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("special",special_command,0);
-@!@:special}{\&{special} primitive@>
-primitive("fontmapfile",special_command,1);
-@!@:fontmapfile}{\&{fontmapfile} primitive@>
-primitive("fontmapline",special_command,2);
-@!@:fontmapline}{\&{fontmapline} 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@>
-primitive("within",within_token,0);@/
-@!@:within_}{\&{within} primitive@>
-primitive("write",write_command,0);@/
-@!@:write_}{\&{write} 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");
-bchar_label:print("||:");
-begin_group:print("begingroup");
-colon:print(":");
-comma:print(",");
-controls:print("controls");
-curl_command:print("curl");
-delimiters:print("delimiters");
-double_colon:print("::");
-end_group:print("endgroup");
-every_job_command:print("everyjob");
-exit_test:print("exitif");
-expand_after:print("expandafter");
-interim_command:print("interim");
-left_brace:print("{");
-left_bracket:print("[");
-let_command:print("let");
-new_internal:print("newinternal");
-of_token:print("of");
-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");
-special_command: if m=2 then print("fontmapline") else
- if m=1 then print("fontmapfile") else
- print("special");
-step_token:print("step");
-str_op:print("str");
-tension:print("tension");
-to_token:print("to");
-until_token:print("until");
-within_token:print("within");
-write_command:print("write");
-
-@ 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 \MP\ 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.
-
-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:=18;
-
-@ We have set aside a two word node beginning at |null| so that we can have
-|value(null)=0|. We will make use of this coincidence later.
-
-@<Initialize table entries...@>=
-link(null):=null;
-value(null):=0;
-
-@ 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,picture_type,pair_type,color_type,
- cmykcolor_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>max_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(""""); 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;
-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 \MP'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 picture 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 \MP\ programs can be simple, like `\.x', or they can
-combine the structural properties of arrays and records, like `\.{x20a.b}'.
-A \MP\ 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 \MP\ 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 \MP\ 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))=name| 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|, as you can well imagine.
-
-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|.
-Finally, variables of type \&{color} have three values in six words
-identified by |red_part_sector|, |green_part_sector|, and |blue_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 red_part_loc(#)==# {where the \&{redpart} is found in a color node}
-@d green_part_loc(#)==#+2 {where the \&{greenpart} is found in a color node}
-@d blue_part_loc(#)==#+4 {where the \&{bluepart} is found in a color node}
-@d cyan_part_loc(#)==# {where the \&{cyanpart} is found in a color node}
-@d magenta_part_loc(#)==#+2 {where the \&{magentapart} is found in a color node}
-@d yellow_part_loc(#)==#+4 {where the \&{yellowpart} is found in a color node}
-@d black_part_loc(#)==#+6 {where the \&{blackpart} is found in a color node}
-@d grey_part_loc(#)==# {where the \&{greypart} is found in a color 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}
-@d color_node_size=6 {the number of words in a color node}
-@d cmykcolor_node_size=8 {the number of words in a color node}
-
-@<Glob...@>=
-@!big_node_size:array[transform_type..pair_type] of small_number;
-@!sector0:array[transform_type..pair_type] of small_number;
-@!sector_offset:array[x_part_sector..black_part_sector] of small_number;
-
-@ The |sector0| array gives for each big node type, |name_type| values
-for its first subfield; the |sector_offset| array gives for each
-|name_type| value, the offset from the first subfield in words;
-and the |big_node_size| array gives the size in words for each type of
-big node.
-
-@<Set init...@>=
-big_node_size[transform_type]:=transform_node_size;
-big_node_size[pair_type]:=pair_node_size;
-big_node_size[color_type]:=color_node_size;
-big_node_size[cmykcolor_type]:=cmykcolor_node_size;
-sector0[transform_type]:=x_part_sector;
-sector0[pair_type]:=x_part_sector;
-sector0[color_type]:=red_part_sector;
-sector0[cmykcolor_type]:=cyan_part_sector;
-for k:=x_part_sector to yy_part_sector do
- sector_offset[k]:=2*(k-x_part_sector);
-for k:=red_part_sector to blue_part_sector do
- sector_offset[k]:=2*(k-red_part_sector);
-for k:=cyan_part_sector to black_part_sector do
- sector_offset[k]:=2*(k-cyan_part_sector);
-
-@ 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):=halfp(s)+sector0[type(p)]; 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");
-red_part_sector: print("red");
-green_part_sector: print("green");
-blue_part_sector: print("blue");
-cyan_part_sector: print("cyan");
-magenta_part_sector: print("magenta");
-yellow_part_sector: print("yellow");
-black_part_sector: print("black");
-grey_part_sector: print("grey");
-capsule: begin print("%CAPSULE"); print_int(p-null); return;
-@.CAPSULE@>
- end;
-end; {there are no other cases}
-print("part "); p:=link(p-sector_offset[name_type(p)]);
-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-sector_offset[t]));
- 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 r:=null;
-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:und_type:=unknown_pen;
-path_type,unknown_path:und_type:=unknown_path;
-picture_type,unknown_picture:und_type:=unknown_picture;
-transform_type,color_type,cmykcolor_type,
-pair_type,numeric_type:und_type:=type(p);
-othercases und_type:=numeric_type; { |known,dependent,proto_dependent,independent| }
-end;
-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 given 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, \MP\ 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 ");
- 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 ");
- 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 \MP\ user specifies a path, \MP\ 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 8-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, and a final supplementary word (of
-which only a quarter is used).
-
-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 x_loc(#) == #+1 {where the |x| coordinate is stored in a knot}
-@d y_loc(#) == #+2 {where the |y| coordinate is stored in a knot}
-@d knot_coord(#) == mem[#].sc {|x| or |y| coordinate given |x_loc| or |y_loc|}
-@d left_coord(#) == mem[#+2].sc
- {coordinate of previous control point given |x_loc| or |y_loc|}
-@d right_coord(#) == mem[#+4].sc
- {coordinate of next control point given |x_loc| or |y_loc|}
-@d knot_node_size=8 {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; \MP\ 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 \MP\ 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 \MP'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 \MP\ should choose the direction}
-
-@ Knots can be user-supplied, or they can be created by program code,
-like the |split_cubic| function, or |copy_path|. The distinction is
-needed for the cleanup routine that runs after |split_cubic|, because
-it should only delete knots it has previously inserted, and never
-anything that was user-supplied. In order to be able to differentiate
-one knot from another, we will set |originator(p):=metapost_user| when
-it appeared in the actual metapost program, and
-|originator(p):=program_code| in all other cases.
-
-@d originator(#) == mem[#+7].hh.b0 {the creator of this knot}
-@d program_code=0 {not created by a user}
-@d metapost_user=1 {created by a user}
-
-@ Here is a routine that prints a given knot list
-in symbolic form. It illustrates the conventions discussed above,
-and checks for anomalies that might arise while \MP\ is being debugged.
-
-@<Declare subroutines for printing expressions@>=
-procedure pr_path(@!h:pointer);
-label done,done1;
-var @!p,@!q:pointer; {for list traversal}
-begin 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;
-
-@ @<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
-\MP'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
-
-@ It is convenient to have another version of |pr_path| that prints the path
-as a diagnostic message.
-
-@<Declare subroutines for printing expressions@>=
-procedure print_path(@!h:pointer;@!s:str_number;@!nuline:boolean);
-begin print_diagnostic("Path",s,nuline); print_ln;
-@.Path at line...@>
-pr_path(h);
-end_diagnostic(true);
-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];
-originator(q):=originator(p);
-copy_knot:=q;
-end;
-
-@ The |copy_path| routine makes a clone of a given path.
-
-@p function copy_path(@!p:pointer):pointer;
-var @!q,@!pp,@!qq:pointer; {for list manipulation}
-begin q:=copy_knot(p);
-qq:=q; pp:=link(p);
-while pp<>p do
- begin link(qq):=copy_knot(pp);@/
- qq:=link(qq);
- pp:=link(pp);
- end;
-link(qq):=q;
-copy_path:=q;
-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);@/
- originator(qq):=originator(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 \MP'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
-else if right_type(p)=endpoint then
- @<Give reasonable values for the unused control points between |p| and~|q|@>;
-p:=q
-
-@ This step makes it possible to transform an explicitly computed path without
-checking the |left_type| and |right_type| fields.
-
-@<Give reasonable values for the unused control points between |p| and~|q|@>=
-begin right_x(p):=x_coord(p); right_y(p):=y_coord(p);@/
-left_x(q):=x_coord(q); left_y(q):=y_coord(q);
-end
-
-@ 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 \MP'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;
-@:MetaPost 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 tridiagonal
-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; r:=null; 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|@>;
- othercases { |given:| }
- @<Calculate the given value of $\theta_n$ and |goto found|@>;
- end;
- 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))\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] Measuring paths.
-\MP's \&{llcorner}, \&{lrcorner}, \&{ulcorner}, and \&{urcorner} operators
-allow the user to measure the bounding box of anything that can go into a
-picture. It's easy to get rough bounds on the $x$ and $y$ extent of a path
-by just finding the bounding box of the knots and the control points. We
-need a more accurate version of the bounding box, but we can still use the
-easy estimate to save time by focusing on the interesting parts of the path.
-
-@ Computing an accurate bounding box involves a theme that will come up again
-and again. 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$.
-
-@ Now here's a subroutine that's handy for all sorts of path computations:
-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
-
-@ Here is a routine that computes the $x$ or $y$ coordinate of the point on
-a cubic corresponding to the |fraction| value~|t|.
-
-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]|.
-
-@d t_of_the_way_end(#)==#,t@=)@>
-@d t_of_the_way(#)==#-take_fraction@=(@>#-t_of_the_way_end
-
-@p function eval_cubic(@!p,@!q:pointer;t:fraction):scaled;
-var @!x1,@!x2,@!x3:scaled; {intermediate values}
-begin x1:=t_of_the_way(knot_coord(p))(right_coord(p));
-x2:=t_of_the_way(right_coord(p))(left_coord(q));
-x3:=t_of_the_way(left_coord(q))(knot_coord(q));@/
-x1:=t_of_the_way(x1)(x2);
-x2:=t_of_the_way(x2)(x3);
-eval_cubic:=t_of_the_way(x1)(x2);
-end;
-
-@ The actual bounding box information is stored in global variables.
-Since it is convenient to address the $x$ and $y$ information
-separately, we define arrays indexed by |x_code..y_code| and use
-macros to give them more convenient names.
-
-@d x_code=0 {index for |minx| and |maxx|}
-@d y_code=1 {index for |miny| and |maxy|}
-@d minx==bbmin[x_code]
-@d maxx==bbmax[x_code]
-@d miny==bbmin[y_code]
-@d maxy==bbmax[y_code]
-
-@<Glob...@>=
-@!bbmin,@!bbmax:array[x_code..y_code] of scaled;
- {the result of procedures that compute bounding box information}
-
-@ Now we're ready for the key part of the bounding box computation.
-The |bound_cubic| procedure updates |bbmin[c]| and |bbmax[c]| based on
-$$B(\hbox{|knot_coord(p)|}, \hbox{|right_coord(p)|},
- \hbox{|left_coord(q)|}, \hbox{|knot_coord(q)|};t)
-$$
-for $0<t\le1$. In other words, the procedure adjusts the bounds to
-accommodate |knot_coord(q)| and any extremes over the range $0<t<1$.
-The |c| parameter is |x_code| or |y_code|.
-
-@p procedure bound_cubic(@!p,@!q:pointer;c:small_number);
-var @!wavy:boolean; {whether we need to look for extremes}
-@!del1,@!del2,@!del3,@!del,@!dmax:scaled; {proportional to the control
- points of a quadratic derived from a cubic}
-@!t,@!tt:fraction; {where a quadratic crosses zero}
-@!x:scaled; {a value that |bbmin[c]| and |bbmax[c]| must accommodate}
-begin x:=knot_coord(q);
-@<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
-@<Check the control points against the bounding box and set |wavy:=true|
- if any of them lie outside@>;
-if wavy then
- begin del1:=right_coord(p)-knot_coord(p);
- del2:=left_coord(q)-right_coord(p);
- del3:=knot_coord(q)-left_coord(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
- begin negate(del1); negate(del2); negate(del3);
- end;
- t:=crossing_point(del1,del2,del3);
- if t<fraction_one then
- @<Test the extremes of the cubic against the bounding box@>;
- end;
-end;
-
-@ @<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>=
-if x<bbmin[c] then bbmin[c]:=x;
-if x>bbmax[c] then bbmax[c]:=x
-
-@ @<Check the control points against the bounding box and set...@>=
-wavy:=true;
-if bbmin[c]<=right_coord(p) then
- if right_coord(p)<=bbmax[c] then
- if bbmin[c]<=left_coord(q) then
- if left_coord(q)<=bbmax[c] then
- wavy:=false
-
-@ If |del1=del2=del3=0|, it's impossible to obey the title of this
-section. We just set |del=0| in that case.
-
-@<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
-
-@ Since |crossing_point| has tried to choose |t| so that
-$B(|del1|,|del2|,|del3|;\tau)$ crosses zero at $\tau=|t|$ with negative
-slope, the value of |del2| computed below should not be positive.
-But rounding error could make it slightly positive in which case we
-must cut it to zero to avoid confusion.
-
-@<Test the extremes of the cubic against the bounding box@>=
-begin x:=eval_cubic(p,q,t);
-@<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
-del2:=t_of_the_way(del2)(del3);
- {now |0,del2,del3| represent the derivative on the remaining interval}
-if del2>0 then del2:=0;
-tt:=crossing_point(0,-del2,-del3);
-if tt<fraction_one then
- @<Test the second extreme against the bounding box@>;
-end
-
-@ @<Test the second extreme against the bounding box@>=
-begin x:=eval_cubic(p,q,t_of_the_way(tt)(fraction_one));
-@<Adjust |bbmin[c]| and |bbmax[c]| to accommodate |x|@>;
-end
-
-@ Finding the bounding box of a path is basically a matter of applying
-|bound_cubic| twice for each pair of adjacent knots.
-
-@p procedure path_bbox(@!h:pointer);
-label exit;
-var @!p,@!q:pointer; {a pair of adjacent knots}
-begin minx:=x_coord(h); miny:=y_coord(h);
-maxx:=minx; maxy:=miny;@/
-p:=h;
-repeat if right_type(p)=endpoint then return;
-q:=link(p);@/
-bound_cubic(x_loc(p),x_loc(q),x_code);
-bound_cubic(y_loc(p),y_loc(q),y_code);
-p:=q;
-until p=h;
-exit:end;
-
-@ Another important way to measure a path is to find its arc length. This
-is best done by using the general bisection algorithm to subdivide the path
-until obtaining ``well behaved'' subpaths whose arc lengths can be approximated
-by simple means.
-
-Since the arc length is the integral with respect to time of the magnitude of
-the velocity, it is natural to use Simpson's rule for the approximation.
-@^Simpson's rule@>
-If $\dot B(t)$ is the spline velocity, Simpson's rule gives
-$$ \vb\dot B(0)\vb + 4\vb\dot B({1\over2})\vb + \vb\dot B(1)\vb \over 6 $$
-for the arc length of a path of length~1. For a cubic spline
-$B(z_0,z_1,z_2,z_3;t)$, the time derivative $\dot B(t)$ is
-$3B(dz_0,dz_1,dz_2;t)$, where $dz_i=z_{i+1}-z_i$. Hence the arc length
-approximation is
-$$ {\vb dz_0\vb \over 2} + 2\vb dz_{02}\vb + {\vb dz_2\vb \over 2}, $$
-where
-$$ dz_{02}={1\over2}\left({dz_0+dz_1\over 2}+{dz_1+dz_2\over 2}\right)$$
-is the result of the bisection algorithm.
-
-@ The remaining problem is how to decide when a subpath is ``well behaved.''
-This could be done via the theoretical error bound for Simpson's rule,
-@^Simpson's rule@>
-but this is impractical because it requires an estimate of the fourth
-derivative of the quantity being integrated. It is much easier to just perform
-a bisection step and see how much the arc length estimate changes. Since the
-error for Simpson's rule is proportional to the fourth power of the sample
-spacing, the remaining error is typically about $1\over16$ of the amount of
-the change. We say ``typically'' because the error has a pseudo-random behavior
-that could cause the two estimates to agree when each contain large errors.
-
-To protect against disasters such as undetected cusps, the bisection process
-should always continue until all the $dz_i$ vectors belong to a single
-$90^\circ$ sector. This ensures that no point on the spline can have velocity
-less than 70\% of the minimum of $\vb dz_0\vb$, $\vb dz_1\vb$ and $\vb dz_2\vb$.
-If such a spline happens to produce an erroneous arc length estimate that
-is little changed by bisection, the amount of the error is likely to be fairly
-small. We will try to arrange things so that freak accidents of this type do
-not destroy the inverse relationship between the \&{arclength} and
-\&{arctime} operations.
-@:arclength_}{\&{arclength} primitive@>
-@:arctime_}{\&{arctime} primitive@>
-
-@ The \&{arclength} and \&{arctime} operations are both based on a recursive
-@^recursion@>
-function that finds the arc length of a cubic spline given $dz_0$, $dz_1$,
-$dz_2$. This |arc_test| routine also takes an arc length goal |a_goal| and
-returns the time when the arc length reaches |a_goal| if there is such a time.
-Thus the return value is either an arc length less than |a_goal| or, if the
-arc length would be at least |a_goal|, it returns a time value decreased by
-|two|. This allows the caller to use the sign of the result to distinguish
-between arc lengths and time values. On certain types of overflow, it is
-possible for |a_goal| and the result of |arc_test| both to be |el_gordo|.
-Otherwise, the result is always less than |a_goal|.
-
-Rather than halving the control point coordinates on each recursive call to
-|arc_test|, it is better to keep them proportional to velocity on the original
-curve and halve the results instead. This means that recursive calls can
-potentially use larger error tolerances in their arc length estimates. How
-much larger depends on to what extent the errors behave as though they are
-independent of each other. To save computing time, we use optimistic assumptions
-and increase the tolerance by a factor of about $\sqrt2$ for each recursive
-call.
-
-In addition to the tolerance parameter, |arc_test| should also have parameters
-for ${1\over3}\vb\dot B(0)\vb$, ${2\over3}\vb\dot B({1\over2})\vb$, and
-${1\over3}\vb\dot B(1)\vb$. These quantities are relatively expensive to compute
-and they are needed in different instances of |arc_test|.
-
-@p @t\4@>@<Declare subroutines needed by |arc_test|@>@;
-function arc_test(@!dx0, @!dy0, @!dx1, @!dy1, @!dx2, @!dy2,
- @!v0, @!v02, @!v2, @!a_goal, @!tol:scaled): scaled;
-label exit;
-var simple: boolean; {are the control points confined to a $90^\circ$ sector?}
-@!dx01, @!dy01, @!dx12, @!dy12, @!dx02, @!dy02: scaled; {bisection results}
-@!v002, @!v022: scaled;
- {twice the velocity magnitudes at $t={1\over4}$ and $t={3\over4}$}
-@!arc: scaled; {best arc length estimate before recursion}
-@<Other local variables in |arc_test|@>@;
-begin @<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,
- |dx2|, |dy2|@>;
-@<Initialize |v002|, |v022|, and the arc length estimate |arc|; if it overflows
- set |arc_test| and |return|@>;
-@<Test if the control points are confined to one quadrant or rotating them
- $45^\circ$ would put them in one quadrant. Then set |simple| appropriately@>;
-if simple and (abs(arc-v02-halfp(v0+v2)) <= tol) then
- if arc < a_goal then @+arc_test := arc
- else @<Estimate when the arc length reaches |a_goal| and set |arc_test| to
- that time minus |two|@>
-else @<Use one or two recursive calls to compute the |arc_test| function@>;
-exit:end;
-
-@ The |tol| value should by multiplied by $\sqrt 2$ before making recursive
-calls, but $1.5$ is an adequate approximation. It is best to avoid using
-|make_fraction| in this inner loop.
-@^inner loop@>
-
-@<Use one or two recursive calls to compute the |arc_test| function@>=
-begin @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is as
- large as possible@>;
-tol := tol + halfp(tol);
-a := arc_test(dx0,dy0, dx01,dy01, dx02,dy02, v0, v002, halfp(v02), a_new, tol);
-if a<0 then @+arc_test := -halfp(two-a)
-else begin @<Update |a_new| to reduce |a_new+a_aux| by |a|@>;
- b := arc_test(dx02,dy02, dx12,dy12, dx2,dy2,
- halfp(v02), v022, v2, a_new, tol);
- if b<0 then @+arc_test := -halfp(-b) - half_unit
- else arc_test := a + half(b-a);
- end;
-end
-
-@ @<Other local variables in |arc_test|@>=
-@!a, @!b: scaled; {results of recursive calls}
-@!a_new, @!a_aux: scaled; {the sum of these gives the |a_goal|}
-
-@ @<Set |a_new| and |a_aux| so their sum is |2*a_goal| and |a_new| is...@>=
-a_aux := el_gordo - a_goal;
-if a_goal > a_aux then
- begin a_aux := a_goal - a_aux;
- a_new := el_gordo;
- end
-else begin a_new := a_goal + a_goal;
- a_aux := 0;
- end
-
-@ There is no need to maintain |a_aux| at this point so we use it as a temporary
-to force the additions and subtractions to be done in an order that avoids
-overflow.
-
-@<Update |a_new| to reduce |a_new+a_aux| by |a|@>=
-if a > a_aux then
- begin a_aux := a_aux - a;
- a_new := a_new + a_aux;
- end
-
-@ This code assumes all {\it dx} and {\it dy} variables have magnitude less than
-|fraction_four|. To simplify the rest of the |arc_test| routine, we strengthen
-this assumption by requiring the norm of each $({\it dx},{\it dy})$ pair to obey
-this bound. Note that recursive calls will maintain this invariant.
-
-@<Bisect the B\'ezier quadratic given by |dx0|, |dy0|, |dx1|, |dy1|,...@>=
-dx01 := half(dx0 + dx1);
-dx12 := half(dx1 + dx2);
-dx02 := half(dx01 + dx12);@/
-dy01 := half(dy0 + dy1);
-dy12 := half(dy1 + dy2);
-dy02 := half(dy01 + dy12)
-
-@ We should be careful to keep |arc<el_gordo| so that calling |arc_test| with
-|a_goal=el_gordo| is guaranteed to yield the arc length.
-
-@<Initialize |v002|, |v022|, and the arc length estimate |arc|;...@>=
-v002 := pyth_add(dx01+half(dx0+dx02), dy01+half(dy0+dy02));
-v022 := pyth_add(dx12+half(dx02+dx2), dy12+half(dy02+dy2));
-tmp := halfp(v02+2);
-arc1 := v002 + half(halfp(v0+tmp) - v002);
-arc := v022 + half(halfp(v2+tmp) - v022);
-if (arc < el_gordo-arc1) then @+arc := arc+arc1
-else begin arith_error := true;
- if a_goal=el_gordo then @+arc_test := el_gordo
- else arc_test := -two;
- return;
- end
-
-@ @<Other local variables in |arc_test|@>=
-tmp, tmp2: scaled; {all purpose temporary registers}
-arc1: scaled; {arc length estimate for the first half}
-
-@ @<Test if the control points are confined to one quadrant or rotating...@>=
-simple := (dx0>=0) and (dx1>=0) and (dx2>=0) or@|
- (dx0<=0) and (dx1<=0) and (dx2<=0);
-if simple then
- simple := (dy0>=0) and (dy1>=0) and (dy2>=0) or@|
- (dy0<=0) and (dy1<=0) and (dy2<=0);
-if not simple then
- begin simple := (dx0>=dy0) and (dx1>=dy1) and (dx2>=dy2) or@|
- (dx0<=dy0) and (dx1<=dy1) and (dx2<=dy2);
- if simple then
- simple := (-dx0>=dy0) and (-dx1>=dy1) and (-dx2>=dy2) or@|
- (-dx0<=dy0) and (-dx1<=dy1) and (-dx2<=dy2);
- end
-
-@ Since Simpson's rule is based on approximating the integrand by a parabola,
-@^Simpson's rule@>
-it is appropriate to use the same approximation to decide when the integral
-reaches the intermediate value |a_goal|. At this point
-$$\eqalign{
- {\vb\dot B(0)\vb\over 3} &= \hbox{|v0|}, \qquad
- {\vb\dot B({1\over4})\vb\over 3} = {\hbox{|v002|}\over 2}, \qquad
- {\vb\dot B({1\over2})\vb\over 3} = {\hbox{|v02|}\over 2}, \cr
- {\vb\dot B({3\over4})\vb\over 3} &= {\hbox{|v022|}\over 2}, \qquad
- {\vb\dot B(1)\vb\over 3} = \hbox{|v2|} \cr
-}
-$$
-and
-$$ {\vb\dot B(t)\vb\over 3} \approx
- \cases{B\left(\hbox{|v0|},
- \hbox{|v002|}-{1\over 2}\hbox{|v0|}-{1\over 4}\hbox{|v02|},
- {1\over 2}\hbox{|v02|}; 2t \right)&
- if $t\le{1\over 2}$\cr
- B\left({1\over 2}\hbox{|v02|},
- \hbox{|v022|}-{1\over 4}\hbox{|v02|}-{1\over 2}\hbox{|v2|},
- \hbox{|v2|}; 2t-1 \right)&
- if $t\ge{1\over 2}$.\cr}
- \eqno (*)
-$$
-We can integrate $\vb\dot B(t)\vb$ by using
-$$\int 3B(a,b,c;\tau)\,dt =
- {B(0,a,a+b,a+b+c;\tau) + {\rm constant} \over {d\tau\over dt}}.
-$$
-
-This construction allows us to find the time when the arc length reaches
-|a_goal| by solving a cubic equation of the form
-$$ B(0,a,a+b,a+b+c;\tau) = x, $$
-where $\tau$ is $2t$ or $2t+1$, $x$ is |a_goal| or |a_goal-arc1|, and $a$, $b$,
-and $c$ are the Bernshte{\u\i}n coefficients from $(*)$ divided by
-@^Bernshte{\u\i}n, Serge{\u\i} Natanovich@>
-$d\tau\over dt$. We shall define a function |solve_rising_cubic| that finds
-$\tau$ given $a$, $b$, $c$, and $x$.
-
-@<Estimate when the arc length reaches |a_goal| and set |arc_test| to...@>=
-begin tmp := (v02 + 2) div 4;
-if a_goal<=arc1 then
- begin tmp2 := halfp(v0);
- arc_test := halfp(solve_rising_cubic(tmp2, arc1-tmp2-tmp, tmp, a_goal))
- - two;
- end
-else begin tmp2 := halfp(v2);
- arc_test := (half_unit - two) +@|
- halfp(solve_rising_cubic(tmp, arc-arc1-tmp-tmp2, tmp2, a_goal-arc1));
- end;
-end
-
-@ Here is the |solve_rising_cubic| routine that finds the time~$t$ when
-$$ B(0, a, a+b, a+b+c; t) = x. $$
-This routine is based on |crossing_point| but is simplified by the
-assumptions that $B(a,b,c;t)\ge0$ for $0\le t\le1$ and that |0<=x<=a+b+c|.
-If rounding error causes this condition to be violated slightly, we just ignore
-it and proceed with binary search. This finds a time when the function value
-reaches |x| and the slope is positive.
-
-@<Declare subroutines needed by |arc_test|@>=
-function solve_rising_cubic(@!a, @!b, @!c, @!x: scaled): scaled;
-var @!ab, @!bc, @!ac: scaled; {bisection results}
-@!t: integer; {$2^k+q$ where unscaled answer is in $[q2^{-k},(q+1)2^{-k})$}
-@!xx: integer; {temporary for updating |x|}
-begin if (a<0) or (c<0) then confusion("rising?");
-@:this can't happen rising?}{\quad rising?@>
-if x<=0 then solve_rising_cubic := 0
-else if x >= a+b+c then solve_rising_cubic := unity
-else begin t := 1;
- @<Rescale if necessary to make sure |a|, |b|, and |c| are all less than
- |el_gordo div 3|@>;
- repeat double(t);
- @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>;
- xx := x - a - ab - ac;
- if xx < -x then
- begin double(x);
- b:=ab; c:=ac;
- end
- else begin x := x + xx;
- a:=ac; b:=bc;
- t := t+1;
- end;
- until t >= unity;
- solve_rising_cubic := t - unity;
- end;
-end;
-
-@ @<Subdivide the B\'ezier quadratic defined by |a|, |b|, |c|@>=
-ab := half(a+b);
-bc := half(b+c);
-ac := half(ab + bc)
-
-@ @d one_third_el_gordo==@'5252525252 {upper bound on |a|, |b|, and |c|}
-
-@<Rescale if necessary to make sure |a|, |b|, and |c| are all less than...@>=
-while (a>one_third_el_gordo) or@| (b>one_third_el_gordo)
- or@| (c>one_third_el_gordo) do
- begin a := halfp(a);
- b := half(b);
- c := halfp(c);
- x := halfp(x);
- end
-
-@ It is convenient to have a simpler interface to |arc_test| that requires no
-unnecessary arguments and ensures that each $({\it dx},{\it dy})$ pair has
-length less than |fraction_four|.
-
-@d arc_tol = 16 {quit when change in arc length estimate reaches this}
-
-@p function do_arc_test(@!dx0, @!dy0, @!dx1, @!dy1, @!dx2, @!dy2,
- @!a_goal: scaled): scaled;
-var @!v0, @!v1, @!v2: scaled; {length of each $({\it dx},{\it dy})$ pair}
-@!v02: scaled; {twice the norm of the quadratic at $t={1\over2}$}
-begin v0 := pyth_add(dx0,dy0);
-v1 := pyth_add(dx1,dy1);
-v2 := pyth_add(dx2,dy2);
-if (v0>=fraction_four) or (v1>=fraction_four) or (v2>=fraction_four) then
- begin arith_error := true;
- if a_goal=el_gordo then @+do_arc_test := el_gordo
- else do_arc_test := -two;
- end
-else begin v02 := pyth_add(dx1+half(dx0+dx2), dy1+half(dy0+dy2));@/
- do_arc_test := arc_test(dx0,dy0, dx1,dy1, dx2,dy2,@|
- v0, v02, v2, a_goal, arc_tol);
- end;
-end;
-
-@ Now it is easy to find the arc length of an entire path.
-
-@p function get_arc_length(@!h: pointer): scaled;
-label done;
-var @!p, @!q: pointer; {for traversing the path}
-@!a, @!a_tot: scaled; {current and total arc lengths}
-begin a_tot := 0;
-p := h;
-while right_type(p)<>endpoint do
- begin q := link(p);
- a := do_arc_test(right_x(p)-x_coord(p), right_y(p)-y_coord(p),@|
- left_x(q)-right_x(p), left_y(q)-right_y(p),@|
- x_coord(q)-left_x(q), y_coord(q)-left_y(q), el_gordo);
- a_tot := slow_add(a, a_tot);
- if q=h then goto done @+else p:=q;
- end;
-done:check_arith;
-get_arc_length := a_tot;
-end;
-
-@ The inverse operation of finding the time on a path~|h| when the arc length
-reaches some value |arc0| can also be accomplished via |do_arc_test|. Some care
-is required to handle very large times or negative times on cyclic paths. For
-non-cyclic paths, |arc0| values that are negative or too large cause
-|get_arc_time| to return 0 or the length of path~|h|.
-
-If |arc0| is greater than the arc length of a cyclic path~|h|, the result is a
-time value greater than the length of the path. Since it could be much greater,
-we must be prepared to compute the arc length of path~|h| and divide this into
-|arc0| to find how many multiples of the length of path~|h| to add.
-
-@p function get_arc_time(@!h: pointer; @!arc0:scaled): scaled;
-label done;
-var @!p, @!q: pointer; {for traversing the path}
-@!t_tot: scaled; {accumulator for the result}
-@!t: scaled; {the result of |do_arc_test|}
-@!arc:scaled; {portion of |arc0| not used up so far}
-@!n: integer; {number of extra times to go around the cycle}
-begin if arc0<0 then @<Deal with a negative |arc0| value and |goto done|@>;
-if arc0=el_gordo then decr(arc0);
-t_tot := 0;
-arc := arc0;
-p := h;
-while (right_type(p)<>endpoint) and (arc>0) do
- begin q := link(p);
- t := do_arc_test(right_x(p)-x_coord(p), right_y(p)-y_coord(p),@|
- left_x(q)-right_x(p), left_y(q)-right_y(p),@|
- x_coord(q)-left_x(q), y_coord(q)-left_y(q), arc);
- @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>;
- if q=h then @<Update |t_tot| and |arc| to avoid going around the cyclic
- path too many times but set |arith_error:=true| and |goto done| on
- overflow@>;
- p := q;
- end;
-done: check_arith;
-get_arc_time := t_tot;
-end;
-
-@ @<Update |arc| and |t_tot| after |do_arc_test| has just returned |t|@>=
-if t<0 then
- begin t_tot := t_tot + t + two;
- arc := 0;
- end
-else begin t_tot := t_tot + unity;
- arc := arc - t;
- end
-
-@ @<Deal with a negative |arc0| value and |goto done|@>=
-begin if left_type(h)=endpoint then t_tot:=0
-else begin p := htap_ypoc(h);
- t_tot := -get_arc_time(p, -arc0);
- toss_knot_list(p);
- end;
-goto done;
-end
-
-@ @<Update |t_tot| and |arc| to avoid going around the cyclic...@>=
-if arc>0 then
- begin n := arc div (arc0 - arc);
- arc := arc - n*(arc0 - arc);
- if t_tot > el_gordo div (n+1) then
- begin arith_error := true;
- t_tot := el_gordo;
- goto done;
- end;
- t_tot := (n + 1)*t_tot;
- end
-
-@* \[20] Data structures for pens.
-A Pen in \MP\ can be either elliptical or polygonal. Elliptical pens result
-in \ps\ \&{stroke} commands, while anything drawn with a polygonal pen is
-@:stroke}{\&{stroke} command@>
-converted into an area fill as described in the next part of this program.
-The mathematics behind this process is based on simple aspects of the theory
-of tracings developed by Leo Guibas, Lyle Ramshaw, and Jorge Stolfi
-[``A kinematic framework for computational geometry,'' Proc.\ IEEE Symp.\
-Foundations of Computer Science {\bf 24} (1983), 100--111].
-
-Polygonal pens are created from paths via \MP's \&{makepen} primitive.
-@:makepen_}{\&{makepen} primitive@>
-This path representation is almost sufficient for our purposes except that
-a pen path should always be a convex polygon with the vertices in
-counter-clockwise order.
-Since we will need to scan pen polygons both forward and backward, a pen
-should be represented as a doubly linked ring of knot nodes. There is
-room for the extra back pointer because we do not need the
-|left_type| or |right_type| fields. In fact, we don't need the |left_x|,
-|left_y|, |right_x|, or |right_y| fields either but we leave these alone
-so that certain procedures can operate on both pens and paths. In particular,
-pens can be copied using |copy_path| and recycled using |toss_knot_list|.
-
-@d knil==info
- {this replaces the |left_type| and |right_type| fields in a pen knot}
-
-@ The |make_pen| procedure turns a path into a pen by initializing
-the |knil| pointers and making sure the knots form a convex polygon.
-Thus each cubic in the given path becomes a straight line and the control
-points are ignored. If the path is not cyclic, the ends are connected by a
-straight line.
-
-@d copy_pen(#)==make_pen(copy_path(#),false)
-
-@p @<Declare a function called |convex_hull|@>@;
-function make_pen(h:pointer;@!need_hull:boolean):pointer;
-var @!p,@!q:pointer; {two consecutive knots}
-begin q:=h;
-repeat p:=q; q:=link(q);
-knil(q):=p;
-until q=h;
-if need_hull then
- begin h:=convex_hull(h);
- @<Make sure |h| isn't confused with an elliptical pen@>;
- end;
-make_pen:=h;
-end;
-
-@ The only information required about an elliptical pen is the overall
-transformation that has been applied to the original \&{pencircle}.
-@:pencircle_}{\&{pencircle} primitive@>
-Since it suffices to keep track of how the three points $(0,0)$, $(1,0)$,
-and $(0,1)$ are transformed, an elliptical pen can be stored in a single
-knot node and transformed as if it were a path.
-
-@d pen_is_elliptical(#)==(#=link(#))
-
-@p function get_pen_circle(@!diam:scaled):pointer;
-var @!h:pointer; {the knot node to return}
-begin h:=get_node(knot_node_size);
-link(h):=h; knil(h):=h;@/
-originator(h):=program_code;@/
-x_coord(h):=0; y_coord(h):=0;@/
-left_x(h):=diam; left_y(h):=0;@/
-right_x(h):=0; right_y(h):=diam;@/
-get_pen_circle:=h;
-end;
-
-@ If the polygon being returned by |make_pen| has only one vertex, it will
-be interpreted as an elliptical pen. This is no problem since a degenerate
-polygon can equally well be thought of as a degenerate ellipse. We need only
-initialize the |left_x|, |left_y|, |right_x|, and |right_y| fields.
-
-@<Make sure |h| isn't confused with an elliptical pen@>=
-if pen_is_elliptical(h) then
- begin left_x(h):=x_coord(h); left_y(h):=y_coord(h);@/
- right_x(h):=x_coord(h); right_y(h):=y_coord(h);
- end
-
-@ We have to cheat a little here but most operations on pens only use
-the first three words in each knot node.
-@^data structure assumptions@>
-
-@<Initialize a pen at |test_pen| so that it fits in nine words@>=
-x_coord(test_pen):=-half_unit;
-y_coord(test_pen):=0;@/
-x_coord(test_pen+3):=half_unit;
-y_coord(test_pen+3):=0;@/
-x_coord(test_pen+6):=0;
-y_coord(test_pen+6):=unity;@/
-link(test_pen):=test_pen+3;
-link(test_pen+3):=test_pen+6;
-link(test_pen+6):=test_pen;
-knil(test_pen):=test_pen+6;
-knil(test_pen+3):=test_pen;
-knil(test_pen+6):=test_pen+3
-
-@ Printing a polygonal pen is very much like printing a path
-
-@<Declare subroutines for printing expressions@>=
-procedure pr_pen(@!h:pointer);
-label done;
-var @!p,@!q:pointer; {for list traversal}
-begin if pen_is_elliptical(h) then
- @<Print the elliptical pen |h|@>
-else begin p:=h;
- repeat print_two(x_coord(p),y_coord(p));
- print_nl(" .. ");
- @<Advance |p| making sure the links are OK and |return| if there is
- a problem@>;
- until p=h;
- print("cycle");
- end;
-done:end;
-
-@ @<Advance |p| making sure the links are OK and |return| if there is...@>=
-q:=link(p);
-if (q=null) or (knil(q)<>p) then
- begin print_nl("???"); goto done; {this won't happen}
-@.???@>
- end;
-p:=q
-
-@ @<Print the elliptical pen |h|@>=
-begin print("pencircle transformed (");
-print_scaled(x_coord(h));
-print_char(",");
-print_scaled(y_coord(h));@/
-print_char(",");
-print_scaled(left_x(h)-x_coord(h));
-print_char(",");
-print_scaled(right_x(h)-x_coord(h));
-print_char(",");
-print_scaled(left_y(h)-y_coord(h));@/
-print_char(",");
-print_scaled(right_y(h)-y_coord(h));@/
-print_char(")");
-end
-
-@ Here us another version of |pr_pen| that prints the pen as a diagnostic
-message.
-
-@<Declare subroutines for printing expressions@>=
-procedure print_pen(@!h:pointer;@!s:str_number;@!nuline:boolean);
-begin print_diagnostic("Pen",s,nuline); print_ln;
-@.Pen at line...@>
-pr_pen(h);
-end_diagnostic(true);
-end;
-
-@ Making a polygonal pen into a path involves restoring the |left_type| and
-|right_type| fields and setting the control points so as to make a polygonal
-path.
-
-@p procedure make_path(@!h:pointer);
-var @!p:pointer; {for traversing the knot list}
-@!k:small_number; {a loop counter}
-@<Other local variables in |make_path|@>@;
-begin if pen_is_elliptical(h) then
- @<Make the elliptical pen |h| into a path@>
-else begin p:=h;
- repeat left_type(p):=explicit;
- right_type(p):=explicit;@/
- @<copy the coordinates of knot |p| into its control points@>;@/
- p:=link(p);
- until p=h;
- end;
-end;
-
-@ @<copy the coordinates of knot |p| into its control points@>=
-left_x(p):=x_coord(p);
-left_y(p):=y_coord(p);@/
-right_x(p):=x_coord(p);
-right_y(p):=y_coord(p)
-
-@ We need an eight knot path to get a good approximation to an ellipse.
-
-@<Make the elliptical pen |h| into a path@>=
-begin @<Extract the transformation parameters from the elliptical pen~|h|@>;
-p:=h;
-for k:=0 to 7 do
- begin @<Initialize |p| as the |k|th knot of a circle of unit diameter,
- transforming it appropriately@>;
- if k=7 then link(p):=h @+else link(p):=get_node(knot_node_size);
- p:=link(p);
- end;
-end
-
-@ @<Extract the transformation parameters from the elliptical pen~|h|@>=
-center_x:=x_coord(h);
-center_y:=y_coord(h);@/
-width_x:=left_x(h)-center_x;
-width_y:=left_y(h)-center_y;@/
-height_x:=right_x(h)-center_x;
-height_y:=right_y(h)-center_y
-
-@ @<Other local variables in |make_path|@>=
-@!center_x,@!center_y:scaled; {translation parameters for an elliptical pen}
-@!width_x,@!width_y:scaled; {the effect of a unit change in $x$}
-@!height_x,@!height_y:scaled; {the effect of a unit change in $y$}
-@!dx,@!dy:scaled; {the vector from knot |p| to its right control point}
-@!kk:integer;
- {|k| advanced $270^\circ$ around the ring (cf. $\sin\theta=\cos(\theta+270)$)}
-
-@ The only tricky thing here are the tables |half_cos| and |d_cos| used to
-find the point $k/8$ of the way around the circle and the direction vector
-to use there.
-
-@<Initialize |p| as the |k|th knot of a circle of unit diameter,...@>=
-kk:=(k+6)mod 8;@/
-x_coord(p):=center_x+take_fraction(half_cos[k],width_x)
- +take_fraction(half_cos[kk],height_x);
-y_coord(p):=center_y+take_fraction(half_cos[k],width_y)
- +take_fraction(half_cos[kk],height_y);
-dx:=-take_fraction(d_cos[kk],width_x)+take_fraction(d_cos[k],height_x);
-dy:=-take_fraction(d_cos[kk],width_y)+take_fraction(d_cos[k],height_y);
-right_x(p):=x_coord(p)+dx;
-right_y(p):=y_coord(p)+dy;@/
-left_x(p):=x_coord(p)-dx;
-left_y(p):=y_coord(p)-dy;@/
-left_type(p):=explicit;
-right_type(p):=explicit;
-originator(p):=program_code
-
-@ @<Glob...@>=
-half_cos:array[0..7] of fraction; {${1\over2}\cos(45k)$}
-d_cos:array[0..7] of fraction; {a magic constant times $\cos(45k)$}
-
-@ The magic constant for |d_cos| is the distance between $({1\over2},0)$ and
-$({1\over4}\sqrt2,{1\over4}\sqrt2)$ times the result of the |velocity|
-function for $\theta=\phi=22.5^\circ$. This comes out to be
-$$ d = {\sqrt{2-\sqrt2}\over 3+3\cos22.5^\circ}
- \approx 0.132608244919772.
-$$
-
-@<Set init...@>=
-half_cos[0]:=fraction_half;
-half_cos[1]:=94906266; {$2^{26}\sqrt2\approx94906265.62$}
-half_cos[2]:=0;@/
-d_cos[0]:=35596755; {$2^{28}d\approx35596754.69$}
-d_cos[1]:=25170707; {$2^{27}\sqrt2\,d\approx25170706.63$}
-d_cos[2]:=0;
-for k:=3 to 4 do
- begin half_cos[k]:=-half_cos[4-k];
- d_cos[k]:=-d_cos[4-k];
- end;
-for k:=5 to 7 do
- begin half_cos[k]:=half_cos[8-k];
- d_cos[k]:=d_cos[8-k];
- end;
-
-@ The |convex_hull| function forces a pen polygon to be convex when it is
-returned by |make_pen| and after any subsequent transformation where rounding
-error might allow the convexity to be lost.
-The convex hull algorithm used here is described by F.~P. Preparata and
-M.~I. Shamos [{\sl Computational Geometry}, Springer-Verlag, 1985].
-
-@<Declare a function called |convex_hull|@>=
-@<Declare a procedure called |move_knot|@>@;
-function convex_hull(@!h:pointer):pointer; {Make a polygonal pen convex}
-label done1,done2,done3;
-var @!l,@!r:pointer; {the leftmost and rightmost knots}
-@!p,@!q:pointer; {knots being scanned}
-@!s:pointer; {the starting point for an upcoming scan}
-@!dx,@!dy:scaled; {a temporary pointer}
-begin if pen_is_elliptical(h) then convex_hull:=h
-else begin @<Set |l| to the leftmost knot in polygon~|h|@>;
- @<Set |r| to the rightmost knot in polygon~|h|@>;
- if l<>r then
- begin s:=link(r);
- @<Find any knots on the path from |l| to |r| above the |l|-|r| line and
- move them past~|r|@>;
- @<Find any knots on the path from |s| to |l| below the |l|-|r| line and
- move them past~|l|@>;
- @<Sort the path from |l| to |r| by increasing $x$@>;
- @<Sort the path from |r| to |l| by decreasing $x$@>;
- end;
- if l<>link(l) then @<Do a Gramm scan and remove vertices where there
- is no left turn@>;
- convex_hull:=l;
- end;
-end;
-
-@ All comparisons are done primarily on $x$ and secondarily on $y$.
-
-@<Set |l| to the leftmost knot in polygon~|h|@>=
-l:=h;
-p:=link(h);
-while p<>h do
- begin if x_coord(p)<=x_coord(l) then
- if (x_coord(p)<x_coord(l)) or (y_coord(p)<y_coord(l)) then
- l:=p;
- p:=link(p);
- end
-
-@ @<Set |r| to the rightmost knot in polygon~|h|@>=
-r:=h;
-p:=link(h);
-while p<>h do
- begin if x_coord(p)>=x_coord(r) then
- if (x_coord(p)>x_coord(r)) or (y_coord(p)>y_coord(r)) then
- r:=p;
- p:=link(p);
- end
-
-@ @<Find any knots on the path from |l| to |r| above the |l|-|r| line...@>=
-dx:=x_coord(r)-x_coord(l);
-dy:=y_coord(r)-y_coord(l);
-p:=link(l);
-while p<>r do
- begin q:=link(p);
- if ab_vs_cd(dx,y_coord(p)-y_coord(l),dy,x_coord(p)-x_coord(l))>0 then
- move_knot(p,r);
- p:=q;
- end
-
-@ The |move_knot| procedure removes |p| from a doubly linked list and inserts
-it after |q|.
-
-@ @<Declare a procedure called |move_knot|@>=
-procedure move_knot(@!p,@!q:pointer);
-begin link(knil(p)):=link(p);
-knil(link(p)):=knil(p);@/
-knil(p):=q;
-link(p):=link(q);
-link(q):=p;
-knil(link(p)):=p;
-end;
-
-@ @<Find any knots on the path from |s| to |l| below the |l|-|r| line...@>=
-p:=s;
-while p<>l do
- begin q:=link(p);
- if ab_vs_cd(dx,y_coord(p)-y_coord(l),dy,x_coord(p)-x_coord(l))<0 then
- move_knot(p,l);
- p:=q;
- end
-
-@ The list is likely to be in order already so we just do linear insertions.
-Secondary comparisons on $y$ ensure that the sort is consistent with the
-choice of |l| and |r|.
-
-@<Sort the path from |l| to |r| by increasing $x$@>=
-p:=link(l);
-while p<>r do
- begin q:=knil(p);
- while x_coord(q)>x_coord(p) do q:=knil(q);
- while x_coord(q)=x_coord(p) do
- if y_coord(q)>y_coord(p) then q:=knil(q) else goto done1;
-done1:
- if q=knil(p) then p:=link(p)
- else begin p:=link(p); move_knot(knil(p),q);
- end;
- end
-
-@ @<Sort the path from |r| to |l| by decreasing $x$@>=
-p:=link(r);
-while p<>l do
- begin q:=knil(p);
- while x_coord(q)<x_coord(p) do q:=knil(q);
- while x_coord(q)=x_coord(p) do
- if y_coord(q)<y_coord(p) then q:=knil(q) else goto done2;
-done2:
- if q=knil(p) then p:=link(p)
- else begin p:=link(p); move_knot(knil(p),q);
- end;
- end
-
-@ The condition involving |ab_vs_cd| tests if there is not a left turn
-at knot |q|. There usually will be a left turn so we streamline the case
-where the |then| clause is not executed.
-
-@<Do a Gramm scan and remove vertices where there...@>=
-begin p:=l; q:=link(l);
-loop @+begin dx:=x_coord(q)-x_coord(p);
- dy:=y_coord(q)-y_coord(p);
- p:=q; q:=link(q);
- if p=l then goto done3;
- if p<>r then
- if ab_vs_cd(dx,y_coord(q)-y_coord(p),dy,x_coord(q)-x_coord(p))<=0 then
- @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>;
- end;
-done3: do_nothing;
-end
-
-@ @<Remove knot |p| and back up |p| and |q| but don't go past |l|@>=
-begin s:=knil(p);
-free_node(p,knot_node_size);
-link(s):=q; knil(q):=s;
-if s=l then p:=s
-else begin p:=knil(s); q:=s;
- end;
-end
-
-@ The |find_offset| procedure sets global variables |(cur_x,cur_y)| to the
-offset associated with the given direction |(x,y)|. If two different offsets
-apply, it chooses one of them.
-
-@p procedure find_offset(@!x,@!y:scaled;@!h:pointer);
-var @!p,@!q:pointer; {consecutive knots}
-@!wx,@!wy,@!hx,@!hy:scaled;
- {the transformation matrix for an elliptical pen}
-@!xx,@!yy:fraction; {untransformed offset for an elliptical pen}
-@!d:fraction; {a temporary register}
-begin if pen_is_elliptical(h) then
- @<Find the offset for |(x,y)| on the elliptical pen~|h|@>
-else begin q:=h;
- repeat p:=q; q:=link(q);
- until ab_vs_cd(x_coord(q)-x_coord(p),y, y_coord(q)-y_coord(p),x)>=0;
- repeat p:=q; q:=link(q);
- until ab_vs_cd(x_coord(q)-x_coord(p),y, y_coord(q)-y_coord(p),x)<=0;
- cur_x:=x_coord(p);
- cur_y:=y_coord(p);
- end;
-end;
-
-@ @<Glob...@>=
-@!cur_x,@!cur_y:scaled; {all-purpose return value registers}
-
-@ @<Find the offset for |(x,y)| on the elliptical pen~|h|@>=
-if (x=0) and (y=0) then
- begin cur_x:=x_coord(h); cur_y:=y_coord(h); @+end
-else begin @<Find the non-constant part of the transformation for |h|@>;
- while (abs(x)<fraction_half) and (abs(y)<fraction_half) do
- begin double(x); double(y); @+end;
- @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the
- untransformed version of |(x,y)|@>;
- cur_x:=x_coord(h)+take_fraction(xx,wx)+take_fraction(yy,hx);
- cur_y:=y_coord(h)+take_fraction(xx,wy)+take_fraction(yy,hy);
- end
-
-@ @<Find the non-constant part of the transformation for |h|@>=
-wx:=left_x(h)-x_coord(h);
-wy:=left_y(h)-y_coord(h);
-hx:=right_x(h)-x_coord(h);
-hy:=right_y(h)-y_coord(h)
-
-@ @<Make |(xx,yy)| the offset on the untransformed \&{pencircle} for the...@>=
-yy:=-(take_fraction(x,hy)+take_fraction(y,-hx));@/
-xx:=take_fraction(x,-wy)+take_fraction(y,wx);@/
-d:=pyth_add(xx,yy);@/
-if d>0 then
- begin xx:=half(make_fraction(xx,d));
- yy:=half(make_fraction(yy,d));
- end
-
-@ Finding the bounding box of a pen is easy except if the pen is elliptical.
-But we can handle that case by just calling |find_offset| twice. The answer
-is stored in the global variables |minx|, |maxx|, |miny|, and |maxy|.
-
-@p procedure pen_bbox(@!h:pointer);
-var @!p:pointer; {for scanning the knot list}
-begin if pen_is_elliptical(h) then
- @<Find the bounding box of an elliptical pen@>
-else begin minx:=x_coord(h); maxx:=minx;
- miny:=y_coord(h); maxy:=miny;@/
- p:=link(h);
- while p<>h do
- begin if x_coord(p)<minx then minx:=x_coord(p);
- if y_coord(p)<miny then miny:=y_coord(p);
- if x_coord(p)>maxx then maxx:=x_coord(p);
- if y_coord(p)>maxy then maxy:=y_coord(p);
- p:=link(p);
- end;
- end;
-end;
-
-@ @<Find the bounding box of an elliptical pen@>=
-begin find_offset(0,fraction_one,h);
-maxx:=cur_x;
-minx:=2*x_coord(h)-cur_x;@/
-find_offset(-fraction_one,0,h);
-maxy:=cur_y;
-miny:=2*y_coord(h)-cur_y;
-end
-
-@* \[21] Edge structures.
-Now we come to \MP's internal scheme for representing pictures.
-The representation is very different from \MF's edge structures
-because \MP\ pictures contain \ps\ graphics objects instead of pixel
-images. However, the basic idea is somewhat similar in that shapes
-are represented via their boundaries.
-
-The main purpose of edge structures is to keep track of graphical objects
-until it is time to translate them into \ps. Since \MP\ does not need to
-know anything about an edge structure other than how to translate it into
-\ps\ and how to find its bounding box, edge structures can be just linked
-lists of graphical objects. \MP\ has no easy way to determine whether
-two such objects overlap, but it suffices to draw the first one first and
-let the second one overwrite it if necessary.
-
-@ Let's consider the types of graphical objects one at a time.
-First of all, a filled contour is represented by a eight-word node. The first
-word contains |type| and |link| fields, and the next six words contain a
-pointer to a cyclic path and the value to use for \ps' \&{currentrgbcolor}
-parameter. If a pen is used for filling |pen_p|, |ljoin_val| and |miterlim_val|
-give the relevant information.
-
-@d path_p(#)==link(#+1)
- {a pointer to the path that needs filling}
-@d pen_p(#)==info(#+1)
- {a pointer to the pen to fill or stroke with}
-@d color_model(#)==type(#+2) { the color model }
-@d obj_red_loc(#)==#+3 {the first of three locations for the color}
-@d obj_cyan_loc==obj_red_loc {the first of four locations for the color}
-@d obj_grey_loc==obj_red_loc {the location for the color}
-@d red_val(#)==mem[#+3].sc
- {the red component of the color in the range $0\ldots1$}
-@d cyan_val==red_val
-@d grey_val==red_val
-@d green_val(#)==mem[#+4].sc
- {the green component of the color in the range $0\ldots1$}
-@d magenta_val==green_val
-@d blue_val(#)==mem[#+5].sc
- {the blue component of the color in the range $0\ldots1$}
-@d yellow_val==blue_val
-@d black_val(#)==mem[#+6].sc
- {the blue component of the color in the range $0\ldots1$}
-@d ljoin_val(#)==name_type(#) {the value of \&{linejoin}}
-@:linejoin_}{\&{linejoin} primitive@>
-@d miterlim_val(#)==mem[#+7].sc {the value of \&{miterlimit}}
-@:miterlimit_}{\&{miterlimit} primitive@>
-@d obj_color_part(#)==mem[#+3-red_part].sc
- {interpret an object pointer that has been offset by |red_part..blue_part|}
-@d pre_script(#)==mem[#+8].hh.lh
-@d post_script(#)==mem[#+8].hh.rh
-@d fill_node_size=9
-@d fill_code=1
-
-@p function new_fill_node(@!p: pointer): pointer;
- {make a fill node for cyclic path |p| and color black}
-var @!t:pointer; {the new node}
-begin t:=get_node(fill_node_size);
- type(t):=fill_code;
- path_p(t):=p;
- pen_p(t):=null; {|null| means don't use a pen}
- red_val(t):=0;
- green_val(t):=0;
- blue_val(t):=0;
- black_val(t):=0;
- color_model(t):=uninitialized_model;
- pre_script(t):=null;
- post_script(t):=null;
- @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>;
- new_fill_node:=t;
-end;
-
-@ @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>=
-if internal[linejoin]>unity then ljoin_val(t):=2
-else if internal[linejoin]>0 then ljoin_val(t):=1
-else ljoin_val(t):=0;
-if internal[miterlimit]<unity then
- miterlim_val(t):=unity
-else miterlim_val(t):=internal[miterlimit]
-
-@ A stroked path is represented by an eight-word node that is like a filled
-contour node except that it contains the current \&{linecap} value, a scale
-factor for the dash pattern, and a pointer that is non-null if the stroke
-is to be dashed. The purpose of the scale factor is to allow a picture to
-be transformed without touching the picture that |dash_p| points to.
-
-@d dash_p(#)==link(#+9)
- {a pointer to the edge structure that gives the dash pattern}
-@d lcap_val(#)==type(#+9)
- {the value of \&{linecap}}
-@:linecap_}{\&{linecap} primitive@>
-@d dash_scale(#)==mem[#+10].sc {dash lengths are scaled by this factor}
-@d stroked_node_size=11
-@d stroked_code=2
-
-@p function new_stroked_node(@!p:pointer): pointer;
- {make a stroked node for path |p| with |pen_p(p)| temporarily |null|}
-var @!t:pointer; {the new node}
-begin t:=get_node(stroked_node_size);
- type(t):=stroked_code;
- path_p(t):=p; pen_p(t):=null;
- dash_p(t):=null;
- dash_scale(t):=unity;
- red_val(t):=0;
- green_val(t):=0;
- blue_val(t):=0;
- black_val(t):=0;
- color_model(t):=uninitialized_model;
- pre_script(t):=null;
- post_script(t):=null;
- @<Set the |ljoin_val| and |miterlim_val| fields in object |t|@>;
- if internal[linecap]>unity then lcap_val(t):=2
- else if internal[linecap]>0 then lcap_val(t):=1
- else lcap_val(t):=0;
- new_stroked_node:=t;
-end;
-
-@ When a dashed line is computed in a transformed coordinate system, the dash
-lengths get scaled like the pen shape and we need to compensate for this. Since
-there is no unique scale factor for an arbitrary transformation, we use the
-the square root of the determinant. The properties of the determinant make it
-easier to maintain the |dash_scale|. The computation is fairly straight-forward
-except for the initialization of the scale factor |s|. The factor of 64 is
-needed because |square_rt| scales its result by $2^8$ while we need $2^{14}$
-to counteract the effect of |take_fraction|.
-
-@<Declare subroutines needed by |print_edges|@>=
-function sqrt_det(a,b,c,d:scaled):scaled;
-var @!maxabs:scaled; {$max(|a|,|b|,|c|,|d|)$}
-@!s:integer; {amount by which the result of |square_rt| needs to be scaled}
-begin @<Initialize |maxabs|@>;
-s:=64;
-while (maxabs<fraction_one) and (s>1) do
- begin double(a); double(b); double(c); double(d);@/
- double(maxabs); s:=halfp(s);
- end;
-sqrt_det:=s*square_rt(abs(take_fraction(a,d)-take_fraction(b,c)));
-end;
-@#
-function get_pen_scale(p:pointer):scaled;
-begin get_pen_scale:=sqrt_det(
- left_x(p)-x_coord(p), right_x(p)-x_coord(p),@/
- left_y(p)-y_coord(p), right_y(p)-y_coord(p));
-end;
-
-@ @<Initialize |maxabs|@>=
-maxabs:=abs(a);
-if abs(b)>maxabs then maxabs:=abs(b);
-if abs(c)>maxabs then maxabs:=abs(c);
-if abs(d)>maxabs then maxabs:=abs(d)
-
-@ When a picture contains text, this is represented by a fourteen-word node
-where the color information and |type| and |link| fields are augmented by
-additional fields that describe the text and how it is transformed.
-The |path_p| and |pen_p| pointers are replaced by a number that identifies
-the font and a string number that gives the text to be displayed.
-The |width|, |height|, and |depth| fields
-give the dimensions of the text at its design size, and the remaining six
-words give a transformation to be applied to the text. The |new_text_node|
-function initializes everything to default values so that the text comes out
-black with its reference point at the origin.
-
-@d text_p(#)==link(#+1) {a string pointer for the text to display}
-@d font_n(#)==info(#+1) {the font number}
-@d width_val(#)==mem[#+7].sc {unscaled width of the text}
-@d height_val(#)==mem[#+9].sc {unscaled height of the text}
-@d depth_val(#)==mem[#+10].sc {unscaled depth of the text}
-@d text_tx_loc(#)==#+11
- {the first of six locations for transformation parameters}
-@d tx_val(#)==mem[#+11].sc {$x$ shift amount}
-@d ty_val(#)==mem[#+12].sc {$y$ shift amount}
-@d txx_val(#)==mem[#+13].sc {|txx| transformation parameter}
-@d txy_val(#)==mem[#+14].sc {|txy| transformation parameter}
-@d tyx_val(#)==mem[#+15].sc {|tyx| transformation parameter}
-@d tyy_val(#)==mem[#+16].sc {|tyy| transformation parameter}
-@d text_trans_part(#)==mem[#+11-x_part].sc
- {interpret a text node ponter that has been offset by |x_part..yy_part|}
-@d text_node_size=17
-@d text_code=3
-
-@p @<Declare text measuring subroutines@>@;
-function new_text_node(f,s:str_number):pointer;
- {make a text node for font |f| and text string |s|}
-var @!t:pointer; {the new node}
-begin t:=get_node(text_node_size);
- type(t):=text_code;
- text_p(t):=s;
- font_n(t):=find_font(f); {this identifies the font}
- red_val(t):=0;
- green_val(t):=0;
- blue_val(t):=0;
- black_val(t):=0;
- color_model(t):=uninitialized_model;
- pre_script(t):=null;
- post_script(t):=null;
- tx_val(t):=0; ty_val(t):=0;
- txx_val(t):=unity; txy_val(t):=0;
- tyx_val(t):=0; tyy_val(t):=unity;
- set_text_box(t); {this finds the bounding box}
- new_text_node:=t;
-end;
-
-@ The last two types of graphical objects that can occur in an edge structure
-are clipping paths and \&{setbounds} paths. These are slightly more difficult
-@:set_bounds_}{\&{setbounds} primitive@>
-to implement because we must keep track of exactly what is being clipped or
-bounded when pictures get merged together. For this reason, each clipping or
-\&{setbounds} operation is represented by a pair of nodes: first comes a
-two-word node whose |path_p| gives the relevant path, then there is the list
-of objects to clip or bound followed by a two-word node whose second word is
-unused.
-
-Using at least two words for each graphical object node allows them all to be
-allocated and deallocated similarly with a global array |gr_object_size| to
-give the size in words for each object type.
-
-@d start_clip_size=2
-@d start_clip_code=4 {|type| of a node that starts clipping}
-@d start_bounds_size=2
-@d start_bounds_code=5 {|type| of a node that gives a \&{setbounds} path}
-@d stop_clip_size=2 {the second word is not used here}
-@d stop_clip_code=6 {|type| of a node that stops clipping}
-@d stop_bounds_size=2 {the second word is not used here}
-@d stop_bounds_code=7 {|type| of a node that stops \&{setbounds}}
-@#
-@d stop_type(#)==(#+2)
- {matching |type| for |start_clip_code| or |start_bounds_code|}
-@d has_color(#)==(type(#)<start_clip_code)
- {does a graphical object have color fields?}
-@d has_pen(#)==(type(#)<text_code)
- {does a graphical object have a |pen_p| field?}
-@d is_start_or_stop(#)==(type(#)>=start_clip_code)
-@d is_stop(#)==(type(#)>=stop_clip_code)
-
-@p function new_bounds_node(@!p:pointer; c:small_number):pointer;
- {make a node of type |c| where |p| is the clipping or \&{setbounds} path}
-var @!t:pointer; {the new node}
-begin t:=get_node(gr_object_size[c]);
- type(t):=c;
- path_p(t):=p;
- new_bounds_node:=t;
-end;
-
-@ We need an array to keep track of the sizes of graphical objects.
-
-@<Glob...@>=
-gr_object_size: array[fill_code..stop_bounds_code] of small_number;
-
-@ @<Set init...@>=
-gr_object_size[fill_code]:=fill_node_size;
-gr_object_size[stroked_code]:=stroked_node_size;
-gr_object_size[text_code]:=text_node_size;
-gr_object_size[start_clip_code]:=start_clip_size;
-gr_object_size[stop_clip_code]:=stop_clip_size;
-gr_object_size[start_bounds_code]:=start_bounds_size;
-gr_object_size[stop_bounds_code]:=stop_bounds_size;
-
-@ All the essential information in an edge structure is encoded as a linked list
-of graphical objects as we have just seen, but it is helpful to add some
-redundant information. A single edge structure might be used as a dash pattern
-many times, and it would be nice to avoid scanning the same structure
-repeatedly. Thus, an edge structure known to be a suitable dash pattern
-has a header that gives a list of dashes in a sorted order designed for rapid
-translation into \ps.
-
-Each dash is represented by a three-word node containing the initial and final
-$x$~coordinates as well as the usual |link| field. The |link| fields points to
-the dash node with the next higher $x$-coordinates and the final link points
-to a special location called |null_dash|. (There should be no overlap between
-dashes). Since the $y$~coordinate of the dash pattern is needed to determine
-the period of repetition, this needs to be stored in the edge header along
-with a pointer to the list of dash nodes.
-
-@d start_x(#)==mem[#+1].sc {the starting $x$~coordinate in a dash node}
-@d stop_x(#)==mem[#+2].sc {the ending $x$~coordinate in a dash node}
-@d dash_node_size=3
-@d dash_list==link
- {in an edge header this points to the first dash node}
-@d dash_y(#)==mem[#+1].sc {$y$ value for the dash list in an edge header}
-
-@ It is also convenient for an edge header to contain the bounding
-box information needed by the \&{llcorner} and \&{urcorner} operators
-so that this does not have to be recomputed unnecessarily. This is done by
-adding fields for the $x$~and $y$ extremes as well as a pointer that indicates
-how far the bounding box computation has gotten. Thus if the user asks for
-the bounding box and then adds some more text to the picture before asking
-for more bounding box information, the second computation need only look at
-the additional text.
-
-When the bounding box has not been computed, the |bblast| pointer points
-to a dummy link at the head of the graphical object list while the |minx_val|
-and |miny_val| fields contain |el_gordo| and the |maxx_val| and |maxy_val|
-fields contain |-el_gordo|.
-
-Since the bounding box of pictures containing objects of type
-|start_bounds_code| depends on the value of \&{truecorners}, the bounding box
-@:true_corners_}{\&{truecorners} primitive@>
-data might not be valid for all values of this parameter. Hence, the |bbtype|
-field is needed to keep track of this.
-
-@d minx_val(#)==mem[#+2].sc
-@d miny_val(#)==mem[#+3].sc
-@d maxx_val(#)==mem[#+4].sc
-@d maxy_val(#)==mem[#+5].sc
-@d bblast(#)==link(#+6) {last item considered in bounding box computation}
-@d bbtype(#)==info(#+6) {tells how bounding box data depends on \&{truecorners}}
-@d dummy_loc(#)==#+7 {where the object list begins in an edge header}
-@d no_bounds=0
- {|bbtype| value when bounding box data is valid for all \&{truecorners} values}
-@d bounds_set=1
- {|bbtype| value when bounding box data is for \&{truecorners}${}\le 0$}
-@d bounds_unset=2
- {|bbtype| value when bounding box data is for \&{truecorners}${}>0$}
-
-@p procedure init_bbox(@!h:pointer);
- {Initialize the bounding box information in edge structure |h|}
-begin bblast(h):=dummy_loc(h);
-bbtype(h):=no_bounds;
-minx_val(h):=el_gordo;
-miny_val(h):=el_gordo;
-maxx_val(h):=-el_gordo;
-maxy_val(h):=-el_gordo;
-end;
-
-@ The only other entries in an edge header are a reference count in the first
-word and a pointer to the tail of the object list in the last word.
-
-@d obj_tail(#)==info(#+7) {points to the last entry in the object list}
-@d edge_header_size=8
-
-@p procedure init_edges(@!h:pointer);
- {initialize an edge header to null values}
-begin dash_list(h):=null_dash;
-obj_tail(h):=dummy_loc(h);
-link(dummy_loc(h)):=null;
-ref_count(h):=null;
-init_bbox(h);
-end;
-
-@ Here is how edge structures are deleted. The process can be recursive because
-of the need to dereference edge structures that are used as dash patterns.
-@^recursion@>
-
-@d add_edge_ref(#)==incr(ref_count(#))
-@d delete_edge_ref(#)==if ref_count(#)=null then toss_edges(#)
- else decr(ref_count(#))
-
-@<Declare the recycling subroutines@>=
-@<Declare subroutines needed by |toss_edges|@>@;
-procedure toss_edges(@!h:pointer);
-var @!p,@!q:pointer; {pointers that scan the list being recycled}
-@!r:pointer; {an edge structure that object |p| refers to}
-begin flush_dash_list(h);
-q:=link(dummy_loc(h));
-while (q<>null) do
- begin p:=q; q:=link(q);
- r:=toss_gr_object(p);
- if r<>null then delete_edge_ref(r);
- end;
-free_node(h,edge_header_size);
-end;
-
-@ @<Declare subroutines needed by |toss_edges|@>=
-procedure flush_dash_list(h:pointer);
-var @!p,@!q:pointer; {pointers that scan the list being recycled}
-begin q:=dash_list(h);
-while q<>null_dash do
- begin p:=q; q:=link(q);
- free_node(p,dash_node_size);
- end;
-dash_list(h):=null_dash;
-end;
-
-@ @<Declare subroutines needed by |toss_edges|@>=
-function toss_gr_object(@!p:pointer):pointer;
- {returns an edge structure that needs to be dereferenced}
-var @!e:pointer; {the edge structure to return}
-begin e:=null;
-@<Prepare to recycle graphical object |p|@>;
-free_node(p,gr_object_size[type(p)]);@/
-toss_gr_object:=e;
-end;
-
-@ @<Prepare to recycle graphical object |p|@>=
-case type(p) of
-fill_code: begin toss_knot_list(path_p(p));
- if pen_p(p)<>null then toss_knot_list(pen_p(p));
- if pre_script(p)<>null then delete_str_ref(pre_script(p));
- if post_script(p)<>null then delete_str_ref(post_script(p));
- end;
-stroked_code: begin toss_knot_list(path_p(p));
- if pen_p(p)<>null then toss_knot_list(pen_p(p));
- if pre_script(p)<>null then delete_str_ref(pre_script(p));
- if post_script(p)<>null then delete_str_ref(post_script(p));
- e:=dash_p(p);
- end;
-text_code: begin
- delete_str_ref(text_p(p));
- if pre_script(p)<>null then delete_str_ref(pre_script(p));
- if post_script(p)<>null then delete_str_ref(post_script(p));
- end;
-start_clip_code,start_bounds_code: toss_knot_list(path_p(p));
-stop_clip_code,stop_bounds_code: do_nothing;
-end; {there are no other cases}
-
-@ If we use |add_edge_ref| to ``copy'' edge structures, the real copying needs
-to be done before making a significant change to an edge structure. Much of
-the work is done in a separate routine |copy_objects| that copies a list of
-graphical objects into a new edge header.
-
-@p @<Declare a function called |copy_objects|@>@;
-function private_edges(h:pointer):pointer;
- {make a private copy of the edge structure headed by |h|}
-var @!hh:pointer; {the edge header for the new copy}
- @!p,@!pp: pointer; {pointers for copying the dash list}
-begin if ref_count(h)=null then private_edges:=h
-else begin decr(ref_count(h));
- hh:=copy_objects(link(dummy_loc(h)),null);
- @<Copy the dash list from |h| to |hh|@>;
- @<Copy the bounding box information from |h| to |hh| and make |bblast(hh)|
- point into the new object list@>;
- private_edges:=hh;
- end;
-end;
-
-@ Here we use the fact that |dash_list(hh)=link(hh)|.
-@^data structure assumptions@>
-
-@<Copy the dash list from |h| to |hh|@>=
-pp:=hh; p:=dash_list(h);
-while (p<>null_dash) do
- begin link(pp):=get_node(dash_node_size);
- pp:=link(pp);@/
- start_x(pp):=start_x(p);
- stop_x(pp):=stop_x(p);
- p:=link(p);
- end;
-link(pp):=null_dash;
-dash_y(hh):=dash_y(h)
-
-@ @<Copy the bounding box information from |h| to |hh|...@>=
-minx_val(hh):=minx_val(h);
-miny_val(hh):=miny_val(h);
-maxx_val(hh):=maxx_val(h);
-maxy_val(hh):=maxy_val(h);@/
-bbtype(hh):=bbtype(h);
-p:=dummy_loc(h); pp:=dummy_loc(hh);
-while(p<>bblast(h)) do
- begin if p=null then confusion("bblast");
-@:this can't happen bblast}{\quad bblast@>
- p:=link(p); pp:=link(pp);
- end;
-bblast(hh):=pp
-
-@ Here is the promised routine for copying graphical objects into a new edge
-structure. It starts copying at object~|p| and stops just before object~|q|.
-If |q| is null, it copies the entire sublist headed at |p|. The resulting edge
-structure requires further initialization by |init_bbox|.
-
-@<Declare a function called |copy_objects|@>=
-function copy_objects(p, q:pointer):pointer;
-var @!hh: pointer; {the new edge header}
- @!pp:pointer; {the last newly copied object}
- @!k:small_number; {temporary register}
-begin hh:=get_node(edge_header_size);
-dash_list(hh):=null_dash;
-ref_count(hh):=null;@/
-pp:=dummy_loc(hh);
-while (p<>q) do
- @<Make |link(pp)| point to a copy of object |p|, and update |p| and |pp|@>;
-obj_tail(hh):=pp;
-link(pp):=null;
-copy_objects:=hh;
-end;
-
-@ @<Make |link(pp)| point to a copy of object |p|, and update |p| and |pp|@>=
-begin k:=gr_object_size[type(p)];@/
-link(pp):=get_node(k);
-pp:=link(pp);
-while (k>0) do
- begin decr(k); mem[pp+k]:=mem[p+k]; @+end;
-@<Fix anything in graphical object |pp| that should differ from the
- corresponding field in |p|@>;
-p:=link(p);
-end
-
-@ @<Fix anything in graphical object |pp| that should differ from the...@>=
-case type(p) of
-start_clip_code,start_bounds_code: path_p(pp):=copy_path(path_p(p));
-fill_code: begin path_p(pp):=copy_path(path_p(p));
- if pre_script(p)<>null then add_str_ref(pre_script(pp));
- if post_script(p)<>null then add_str_ref(post_script(pp));
- if pen_p(p)<>null then pen_p(pp):=copy_pen(pen_p(p));
- end;
-stroked_code: begin path_p(pp):=copy_path(path_p(p));
- if pre_script(p)<>null then add_str_ref(pre_script(pp));
- if post_script(p)<>null then add_str_ref(post_script(pp));
- pen_p(pp):=copy_pen(pen_p(p));
- if dash_p(p)<>null then add_edge_ref(dash_p(pp));
- end;
-text_code: begin
- if pre_script(p)<>null then add_str_ref(pre_script(pp));
- if post_script(p)<>null then add_str_ref(post_script(pp));
- add_str_ref(text_p(pp));
- end;
-stop_clip_code,stop_bounds_code: do_nothing;
-end {there are no other cases}
-
-@ Here is one way to find an acceptable value for the second argument to
-|copy_objects|. Given a non-null graphical object list, |skip_1component|
-skips past one picture component, where a ``picture component'' is a single
-graphical object, or a start bounds or start clip object and everything up
-through the matching stop bounds or stop clip object. The macro version avoids
-procedure call overhead and error handling: |skip_component(p)(e)| advances |p|
-unless |p| points to a stop bounds or stop clip node, in which case it executes
-|e| instead.
-
-@d skip_component(#)==if not is_start_or_stop(#) then #:=link(#)
- else if not is_stop(#) then #:=skip_1component(#)
- else skipc_end
-@d skipc_end(#)==#
-
-@p function skip_1component(p:pointer):pointer;
-var @!lev:integer; {current nesting level}
-begin lev:=0;
-repeat if is_start_or_stop(p) then
- if is_stop(p) then decr(lev) @+else incr(lev);
-p:=link(p);
-until lev=0;
-skip_1component:=p;
-end;
-
-@ Here is a diagnostic routine for printing an edge structure in symbolic form.
-
-@<Declare subroutines for printing expressions@>=
-@<Declare subroutines needed by |print_edges|@>@;
-procedure print_edges(@!h:pointer;@!s:str_number;@!nuline:boolean);
-var @!p:pointer; {a graphical object to be printed}
-@!hh,@!pp:pointer; {temporary pointers}
-@!scf:scaled; {a scale factor for the dash pattern}
-@!ok_to_dash:boolean; {|false| for polygonal pen strokes}
-begin print_diagnostic("Edge structure",s,nuline);
-p:=dummy_loc(h);
-while link(p)<>null do
- begin p:=link(p);
- print_ln;
- case type(p) of
- @<Cases for printing graphical object node |p|@>@;
- othercases begin print("[unknown object type!]");
- end
- endcases;@/
- end;
-print_nl("End edges");
-if p<>obj_tail(h) then print("?");
-@.End edges?@>
-end_diagnostic(true);
-end;
-
-@ @<Cases for printing graphical object node |p|@>=
-fill_code: begin print("Filled contour ");
- print_obj_color(p);
- print_char(":"); print_ln;
- pr_path(path_p(p)); print_ln;
- if (pen_p(p)<>null) then
- begin @<Print join type for graphical object |p|@>;
- print(" with pen"); print_ln;
- pr_pen(pen_p(p));
- end;
- end;
-
-@ @<Print join type for graphical object |p|@>=
-case ljoin_val(p) of
-0:begin print("mitered joins limited ");
- print_scaled(miterlim_val(p));
- end;
-1:print("round joins");
-2:print("beveled joins");
-othercases print("?? joins");
-@.??@>
-endcases
-
-@ For stroked nodes, we need to print |lcap_val(p)| as well.
-
-@<Print join and cap types for stroked node |p|@>=
-case lcap_val(p) of
-0:print("butt");
-1:print("round");
-2:print("square");
-othercases print("??")
-@.??@>
-endcases;
-print(" ends, ");
-@<Print join type for graphical object |p|@>
-
-@ Here is a routine that prints the color of a graphical object if it isn't
-black (the default color).
-
-@<Declare subroutines needed by |print_edges|@>=
-@<Declare a procedure called |print_compact_node|@>@;
-procedure print_obj_color(@!p:pointer);
-begin if color_model(p)=grey_model then
- if grey_val(p)>0 then
- begin print("greyed ");
- print_compact_node(obj_grey_loc(p),1);
- end
- else if color_model(p)=cmyk_model then
- if (cyan_val(p)>0) or (magenta_val(p)>0) or (yellow_val(p)>0) or (black_val(p)>0) then
- begin print("colored ");
- print_compact_node(obj_cyan_loc(p),4);
- end
- else if color_model(p)=rgb_model then
- if (red_val(p)>0) or (green_val(p)>0) or (blue_val(p)>0) then
- begin print("processcolored ");
- print_compact_node(obj_red_loc(p),3);
- end;
-end;
-
-@ We also need a procedure for printing consecutive scaled values as if they
-were a known big node.
-
-@<Declare a procedure called |print_compact_node|@>=
-procedure print_compact_node(@!p:pointer;k:small_number);
-var @!q:pointer; {last location to print}
-begin q:=p+k-1;
-print_char("(");
-while p<=q do
- begin print_scaled(mem[p].sc);
- if p<q then print_char(",");
- incr(p);
- end;
-print_char(")");
-end;
-
-@ @<Cases for printing graphical object node |p|@>=
-stroked_code: begin print("Filled pen stroke ");
- print_obj_color(p);
- print_char(":"); print_ln;
- pr_path(path_p(p));
- if dash_p(p)<>null then
- begin print_nl("dashed (");
- @<Finish printing the dash pattern that |p| refers to@>;
- end;
- print_ln;
- @<Print join and cap types for stroked node |p|@>;
- print(" with pen"); print_ln;
- if pen_p(p)=null then print("???") {shouldn't happen}
-@.???@>
- else pr_pen(pen_p(p));
- end;
-
-@ Normally, the |dash_list| field in an edge header is set to |null_dash|
-when it is not known to define a suitable dash pattern. This is disallowed
-here because the |dash_p| field should never point to such an edge header.
-Note that memory is allocated for |start_x(null_dash)| and we are free to
-give it any convenient value.
-
-@<Finish printing the dash pattern that |p| refers to@>=
-ok_to_dash:=pen_is_elliptical(pen_p(p));
-if not ok_to_dash then scf:=unity
-else scf:=dash_scale(p);
-hh:=dash_p(p);
-pp:=dash_list(hh);
-if (pp=null_dash) or (dash_y(hh)<0) then print(" ??")
-else begin start_x(null_dash):=start_x(pp)+dash_y(hh);
- while pp<>null_dash do
- begin print("on ");
- print_scaled(take_scaled(stop_x(pp)-start_x(pp),scf));
- print(" off ");
- print_scaled(take_scaled(start_x(link(pp))-stop_x(pp),scf));
- pp := link(pp);
- if pp<>null_dash then print_char(" ");
- end;
- print(") shifted ");
- print_scaled(-take_scaled(dash_offset(hh),scf));
- if not ok_to_dash or (dash_y(hh)=0) then print(" (this will be ignored)");
- end
-
-@ @<Declare subroutines needed by |print_edges|@>=
-function dash_offset(h:pointer):scaled;
-var @!x:scaled; {the answer}
-begin if (dash_list(h)=null_dash) or (dash_y(h)<0) then confusion("dash0");
-@:this can't happen dash0}{\quad dash0@>
-if dash_y(h)=0 then x:=0
-else begin x:=-(start_x(dash_list(h)) mod dash_y(h));
- if x<0 then x:=x+dash_y(h);
- end;
-dash_offset:=x;
-end;
-
-@ @<Cases for printing graphical object node |p|@>=
-text_code: begin print_char(""""); print(text_p(p));
- print(""" infont """); print(font_name[font_n(p)]);
- print_char(""""); print_ln;
- print_obj_color(p);
- print("transformed ");
- print_compact_node(text_tx_loc(p),6);
-end;
-
-@ @<Cases for printing graphical object node |p|@>=
-start_clip_code: begin print("clipping path:");
- print_ln;
- pr_path(path_p(p));
- end;
-stop_clip_code: print("stop clipping");
-
-@ @<Cases for printing graphical object node |p|@>=
-start_bounds_code: begin print("setbounds path:");
- print_ln;
- pr_path(path_p(p));
- end;
-stop_bounds_code: print("end of setbounds");
-
-@ To initialize the |dash_list| field in an edge header~|h|, we need a
-subroutine that scans an edge structure and tries to interpret it as a dash
-pattern. This can only be done when there are no filled regions or clipping
-paths and all the pen strokes have the same color. The first step is to let
-$y_0$ be the initial $y$~coordinate of the first pen stroke. Then we implicitly
-project all the pen stroke paths onto the line $y=y_0$ and require that there
-be no retracing. If the resulting paths cover a range of $x$~coordinates of
-length $\Delta x$, we set |dash_y(h)| to the length of the dash pattern by
-finding the maximum of $\Delta x$ and the absolute value of~$y_0$.
-
-@p @<Declare a procedure called |x_retrace_error|@>@;
-function make_dashes(h:pointer):pointer; {returns |h| or |null|}
-label exit, found, not_found;
-var @!p:pointer; {this scans the stroked nodes in the object list}
-@!y0:scaled; {the initial $y$ coordinate}
-@!p0:pointer; {if not |null| this points to the first stroked node}
-@!pp,@!qq,@!rr:pointer; {pointers into |path_p(p)|}
-@!d,@!dd:pointer; {pointers used to create the dash list}
-@<Other local variables in |make_dashes|@>@;
-begin if dash_list(h)<>null_dash then goto found;
-p0:=null; y0:=0;
-p:=link(dummy_loc(h));
-while p<>null do
- begin if type(p)<>stroked_code then
- @<Compain that the edge structure contains a node of the wrong type
- and |goto not_found|@>;
- pp:=path_p(p);
- if p0=null then
- begin p0:=p; y0:=y_coord(pp); @+end;
- @<Make |d| point to a new dash node created from stroke |p| and path |pp|
- or |goto not_found| if there is an error@>;
- @<Insert |d| into the dash list and |goto not_found| if there is an error@>;
- p:=link(p);
- end;
-if dash_list(h)=null_dash then goto not_found; {No error message}
-@<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>;
-@<Set |dash_y(h)| and merge the first and last dashes if necessary@>;
-found:make_dashes:=h; return;
-not_found: @<Flush the dash list, recycle |h| and return |null|@>;
-exit:end;
-
-@ @<Compain that the edge structure contains a node of the wrong type...@>=
-begin print_err("Picture is too complicated to use as a dash pattern");
-help3("When you say `dashed p', picture p should not contain any")@/
- ("text, filled regions, or clipping paths. This time it did")@/
- ("so I'll just make it a solid line instead.");@/
-put_get_error;
-goto not_found;
-end
-
-@ A similar error occurs when monotonicity fails.
-
-@<Declare a procedure called |x_retrace_error|@>=
-procedure x_retrace_error;
-begin print_err("Picture is too complicated to use as a dash pattern");
-help3("When you say `dashed p', every path in p should be monotone")@/
- ("in x and there must be no overlapping. This failed")@/
- ("so I'll just make it a solid line instead.");
-put_get_error;
-end;
-
-@ We stash |p| in |info(d)| if |dash_p(p)<>0| so that subsequent processing can
-handle the case where the pen stroke |p| is itself dashed.
-
-@<Make |d| point to a new dash node created from stroke |p| and path...@>=
-@<Make sure |p| and |p0| are the same color and |goto not_found| if there is
- an error@>;
-rr:=pp;
-if link(pp)<>pp then
- repeat qq:=rr; rr:=link(rr);
- @<Check for retracing between knots |qq| and |rr| and |goto not_found|
- if there is a problem@>;
- until right_type(rr)=endpoint;
-d:=get_node(dash_node_size);
-if dash_p(p)=0 then info(d):=0 @+else info(d):=p;
-if x_coord(pp)<x_coord(rr) then
- begin start_x(d):=x_coord(pp);
- stop_x(d):=x_coord(rr);
- end
-else begin start_x(d):=x_coord(rr);
- stop_x(d):=x_coord(pp);
- end;
-
-@ We also need to check for the case where the segment from |qq| to |rr| is
-monotone in $x$ but is reversed relative to the path from |pp| to |qq|.
-
-@<Check for retracing between knots |qq| and |rr| and |goto not_found|...@>=
-x0:=x_coord(qq);
-x1:=right_x(qq);
-x2:=left_x(rr);
-x3:=x_coord(rr);
-if (x0>x1) or (x1>x2) or (x2>x3) then
- if (x0<x1) or (x1<x2) or (x2<x3) then
- if ab_vs_cd(x2-x1,x2-x1,x1-x0,x3-x2)>0 then
- begin x_retrace_error; goto not_found;
- end;
-if (x_coord(pp)>x0) or (x0>x3) then
- if (x_coord(pp)<x0) or (x0<x3) then
- begin x_retrace_error; goto not_found;
- end
-
-@ @<Other local variables in |make_dashes|@>=
-@!x0,@!x1,@!x2,@!x3:scaled; {$x$ coordinates of the segment from |qq| to |rr|}
-
-@ @<Make sure |p| and |p0| are the same color and |goto not_found|...@>=
-if (red_val(p)<>red_val(p0)) or (black_val(p)<>black_val(p0)) or@|
- (green_val(p)<>green_val(p0)) or (blue_val(p)<>blue_val(p0)) then
- begin print_err("Picture is too complicated to use as a dash pattern");
- help3("When you say `dashed p', everything in picture p should")@/
- ("be the same color. I can't handle your color changes")@/
- ("so I'll just make it a solid line instead.");@/
- put_get_error;
- goto not_found;
- end
-
-@ @<Insert |d| into the dash list and |goto not_found| if there is an error@>=
-start_x(null_dash):=stop_x(d);
-dd:=h; {this makes |link(dd)=dash_list(h)|}
-while start_x(link(dd))<stop_x(d) do
- dd:=link(dd);
-if dd<>h then
- if (stop_x(dd)>start_x(d)) then
- begin x_retrace_error; goto not_found; @+end;
-link(d):=link(dd);
-link(dd):=d
-
-@ @<Set |dash_y(h)| and merge the first and last dashes if necessary@>=
-d:=dash_list(h);
-while (link(d)<>null_dash) do
- d:=link(d);
-dd:=dash_list(h);
-dash_y(h):=stop_x(d)-start_x(dd);
-if abs(y0)>dash_y(h) then
- dash_y(h):=abs(y0)
-else if d<>dd then
- begin dash_list(h):=link(dd);
- stop_x(d):=stop_x(dd)+dash_y(h);
- free_node(dd,dash_node_size);
- end
-
-@ We get here when the argument is a null picture or when there is an error.
-Recovering from an error involves making |dash_list(h)| empty to indicate
-that |h| is not known to be a valid dash pattern. We also dereference |h|
-since it is not being used for the return value.
-
-@<Flush the dash list, recycle |h| and return |null|@>=
-flush_dash_list(h);
-delete_edge_ref(h);
-make_dashes:=null
-
-@ Having carefully saved the dashed stroked nodes in the
-corresponding dash nodes, we must be prepared to break up these dashes into
-smaller dashes.
-
-@<Scan |dash_list(h)| and deal with any dashes that are themselves dashed@>=
-d:=h; {now |link(d)=dash_list(h)|}
-while link(d)<>null_dash do
- begin ds:=info(link(d));
- if ds=null then d:=link(d)
- else begin
- hh:=dash_p(ds);
- hsf:=dash_scale(ds);
- if (hh=null) then confusion("dash1");
-@:this can't happen dash0}{\quad dash1@>
- if dash_y(hh)=0 then d:=link(d)
- else begin if dash_list(hh)=null then confusion("dash1");
-@:this can't happen dash0}{\quad dash1@>
- @<Replace |link(d)| by a dashed version as determined by edge header
- |hh| and scale factor |ds|@>;
- end;
- end;
- end
-
-@ @<Other local variables in |make_dashes|@>=
-@!dln:pointer; {|link(d)|}
-@!hh:pointer; {an edge header that tells how to break up |dln|}
-@!hsf:scaled; {the dash pattern from |hh| gets scaled by this}
-@!ds:pointer; {the stroked node from which |hh| and |hsf| are derived}
-@!xoff:scaled; {added to $x$ values in |dash_list(hh)| to match |dln|}
-
-@ @<Replace |link(d)| by a dashed version as determined by edge header...@>=
-dln:=link(d);
-dd:=dash_list(hh);
-xoff:=start_x(dln)-take_scaled(hsf,start_x(dd))-
- take_scaled(hsf,dash_offset(hh));
-start_x(null_dash):=take_scaled(hsf,start_x(dd))+take_scaled(hsf,dash_y(hh));
-stop_x(null_dash):=start_x(null_dash);
-@<Advance |dd| until finding the first dash that overlaps |dln| when
- offset by |xoff|@>;
-while start_x(dln)<=stop_x(dln) do
- begin @<If |dd| has `fallen off the end', back up to the beginning and fix
- |xoff|@>;
- @<Insert a dash between |d| and |dln| for the overlap with the offset version
- of |dd|@>;
- dd:=link(dd);
- start_x(dln):=xoff+take_scaled(hsf,start_x(dd));
- end;
-link(d):=link(dln);
-free_node(dln,dash_node_size)
-
-@ The name of this module is a bit of a lie because we actually just find the
-first |dd| where |take_scaled(hsf,stop_x(dd))| is large enough to make an
-overlap possible. It could be that the unoffset version of dash |dln| falls
-in the gap between |dd| and its predecessor.
-
-@<Advance |dd| until finding the first dash that overlaps |dln| when...@>=
-while xoff+take_scaled(hsf,stop_x(dd))<start_x(dln) do
- dd:=link(dd)
-
-@ @<If |dd| has `fallen off the end', back up to the beginning and fix...@>=
-if dd=null_dash then
- begin dd:=dash_list(hh);
- xoff:=xoff+take_scaled(hsf,dash_y(hh));
- end
-
-@ At this point we already know that
-|start_x(dln)<=xoff+take_scaled(hsf,stop_x(dd))|.
-
-@<Insert a dash between |d| and |dln| for the overlap with the offset...@>=
-if xoff+take_scaled(hsf,start_x(dd))<=stop_x(dln) then
- begin link(d):=get_node(dash_node_size);
- d:=link(d);
- link(d):=dln;
- if start_x(dln)>xoff+take_scaled(hsf,start_x(dd))
- then start_x(d):=start_x(dln)
- else start_x(d):=xoff+take_scaled(hsf,start_x(dd));
- if stop_x(dln)<xoff+take_scaled(hsf,stop_x(dd)) then stop_x(d):=stop_x(dln)
- else stop_x(d):=xoff+take_scaled(hsf,stop_x(dd));
- end
-
-@ The next major task is to update the bounding box information in an edge
-header~|h|. This is done via a procedure |adjust_bbox| that enlarges an edge
-header's bounding box to accommodate the box computed by |path_bbox| or
-|pen_bbox|. (This is stored in global variables |minx|, |miny|, |maxx|, and
-|maxy|.)
-
-@p procedure adjust_bbox(h:pointer);
-begin if minx<minx_val(h) then minx_val(h):=minx;
-if miny<miny_val(h) then miny_val(h):=miny;
-if maxx>maxx_val(h) then maxx_val(h):=maxx;
-if maxy>maxy_val(h) then maxy_val(h):=maxy;
-end;
-
-@ Here is a special routine for updating the bounding box information in
-edge header~|h| to account for the squared-off ends of a non-cyclic path~|p|
-that is to be stroked with the pen~|pp|.
-
-@p procedure box_ends(@!p, @!pp, @!h:pointer);
-label exit;
-var @!q:pointer; {a knot node adjacent to knot |p|}
-@!dx,@!dy:fraction; {a unit vector in the direction out of the path at~|p|}
-@!d:scaled; {a factor for adjusting the length of |(dx,dy)|}
-@!z:scaled; {a coordinate being tested against the bounding box}
-@!xx,@!yy:scaled; {the extreme pen vertex in the |(dx,dy)| direction}
-@!i:integer; {a loop counter}
-begin if right_type(p)<>endpoint then
- begin q:=link(p);
- loop @+begin @<Make |(dx,dy)| the final direction for the path segment from
- |q| to~|p|; set~|d|@>;
- d:=pyth_add(dx,dy);
- if d>0 then
- begin @<Normalize the direction |(dx,dy)| and find the pen offset
- |(xx,yy)|@>;
- for i:=1 to 2 do
- begin @<Use |(dx,dy)| to generate a vertex of the square end cap and
- update the bounding box to accommodate it@>;@/
- dx:=-dx; dy:=-dy;
- end;
- end;
- if right_type(p)=endpoint then return
- else @<Advance |p| to the end of the path and make |q| the previous knot@>;
- end;
- end;
-exit: ;
-end;
-
-@ @<Make |(dx,dy)| the final direction for the path segment from...@>=
-if q=link(p) then
- begin dx:=x_coord(p)-right_x(p);
- dy:=y_coord(p)-right_y(p);
- if (dx=0)and(dy=0) then
- begin dx:=x_coord(p)-left_x(q);
- dy:=y_coord(p)-left_y(q);
- end;
- end
-else begin dx:=x_coord(p)-left_x(p);
- dy:=y_coord(p)-left_y(p);
- if (dx=0)and(dy=0) then
- begin dx:=x_coord(p)-right_x(q);
- dy:=y_coord(p)-right_y(q);
- end;
- end;
-dx:=x_coord(p)-x_coord(q);
-dy:=y_coord(p)-y_coord(q)
-
-@ @<Normalize the direction |(dx,dy)| and find the pen offset |(xx,yy)|@>=
-dx:=make_fraction(dx,d);
-dy:=make_fraction(dy,d);@/
-find_offset(-dy,dx,pp);
-xx:=cur_x; yy:=cur_y
-
-@ @<Use |(dx,dy)| to generate a vertex of the square end cap and...@>=
-find_offset(dx,dy,pp);
-d:=take_fraction(xx-cur_x,dx)+take_fraction(yy-cur_y,dy);
-if (d<0)and(i=1) or (d>0)and(i=2) then confusion("box_ends");
-@:this can't happen box ends}{\quad\\{box_ends}@>
-z:=x_coord(p)+cur_x+take_fraction(d,dx);
-if z<minx_val(h) then minx_val(h):=z;
-if z>maxx_val(h) then maxx_val(h):=z;
-z:=y_coord(p)+cur_y+take_fraction(d,dy);
-if z<miny_val(h) then miny_val(h):=z;
-if z>maxy_val(h) then maxy_val(h):=z
-
-@ @<Advance |p| to the end of the path and make |q| the previous knot@>=
-repeat q:=p;
-p:=link(p);
-until right_type(p)=endpoint
-
-@ The major difficulty in finding the bounding box of an edge structure is the
-effect of clipping paths. We treat them conservatively by only clipping to the
-clipping path's bounding box, but this still
-requires recursive calls to |set_bbox| in order to find the bounding box of
-@^recursion@>
-the objects to be clipped. Such calls are distinguished by the fact that the
-boolean parameter |top_level| is false.
-
-@p procedure set_bbox(@!h:pointer;top_level:boolean);
-label exit;
-var @!p:pointer; {a graphical object being considered}
-@!sminx,@!sminy,@!smaxx,@!smaxy:scaled;
- {for saving the bounding box during recursive calls}
-@!x0,@!x1,@!y0,@!y1:scaled; {temporary registers}
-@!lev:integer; {nesting level for |start_bounds_code| nodes}
-begin @<Wipe out any existing bounding box information if |bbtype(h)| is
- incompatible with |internal[true_corners]|@>;
-while link(bblast(h))<>null do
- begin p:=link(bblast(h));
- bblast(h):=p;
- case type(p) of
- stop_clip_code: if top_level then confusion("bbox") @+else return;
-@:this can't happen bbox}{\quad bbox@>
- @<Other cases for updating the bounding box based on the type of object |p|@>@;
- end; {all cases are enumerated above}
- end;
-if not top_level then confusion("bbox");
-exit:end;
-
-@ @<Wipe out any existing bounding box information if |bbtype(h)| is...@>=
-case bbtype(h) of
-no_bounds: do_nothing;
-bounds_set: if internal[true_corners]>0 then init_bbox(h);
-bounds_unset: if internal[true_corners]<=0 then init_bbox(h);
-end {there are no other cases}
-
-@ @<Other cases for updating the bounding box...@>=
-fill_code: begin path_bbox(path_p(p));
- if pen_p(p)<>null then
- begin x0:=minx; y0:=miny;
- x1:=maxx; y1:=maxy;
- pen_bbox(pen_p(p));
- minx:=minx+x0;
- miny:=miny+y0;
- maxx:=maxx+x1;
- maxy:=maxy+y1;
- end;
- adjust_bbox(h);
- end;
-
-@ @<Other cases for updating the bounding box...@>=
-start_bounds_code: if internal[true_corners]>0 then bbtype(h):=bounds_unset
- else begin bbtype(h):=bounds_set;
- path_bbox(path_p(p));
- adjust_bbox(h);
- @<Scan to the matching |stop_bounds_code| node and update |p| and
- |bblast(h)|@>;
- end;
-stop_bounds_code: if internal[true_corners]<=0 then confusion("bbox2");
-@:this can't happen bbox2}{\quad bbox2@>
-
-@ @<Scan to the matching |stop_bounds_code| node and update |p| and...@>=
-lev:=1;
-while lev<>0 do
- begin if link(p)=null then confusion("bbox2");
-@:this can't happen bbox2}{\quad bbox2@>
- p:=link(p);
- if type(p)=start_bounds_code then incr(lev)
- else if type(p)=stop_bounds_code then decr(lev);
- end;
-bblast(h):=p
-
-@ It saves a lot of grief here to be slightly conservative and not account for
-omitted parts of dashed lines. We also don't worry about the material omitted
-when using butt end caps. The basic computation is for round end caps and
-|box_ends| augments it for square end caps.
-
-@<Other cases for updating the bounding box...@>=
-stroked_code: begin path_bbox(path_p(p));
- x0:=minx; y0:=miny;
- x1:=maxx; y1:=maxy;
- pen_bbox(pen_p(p));
- minx:=minx+x0;
- miny:=miny+y0;
- maxx:=maxx+x1;
- maxy:=maxy+y1;
- adjust_bbox(h);
- if (left_type(path_p(p))=endpoint)and(lcap_val(p)=2) then
- box_ends(path_p(p), pen_p(p), h);
- end;
-
-@ The height width and depth information stored in a text node determines a
-rectangle that needs to be transformed according to the transformation
-parameters stored in the text node.
-
-@<Other cases for updating the bounding box...@>=
-text_code: begin x1:=take_scaled(txx_val(p),width_val(p));
- y0:=take_scaled(txy_val(p),-depth_val(p));
- y1:=take_scaled(txy_val(p),height_val(p));
- minx:=tx_val(p);
- maxx:=minx;
- if y0<y1 then
- begin minx:=minx+y0; maxx:=maxx+y1; @+end
- else begin minx:=minx+y1; maxx:=maxx+y0; @+end;
- if x1<0 then minx:=minx+x1 @+else maxx:=maxx+x1;
- x1:=take_scaled(tyx_val(p),width_val(p));
- y0:=take_scaled(tyy_val(p),-depth_val(p));
- y1:=take_scaled(tyy_val(p),height_val(p));
- miny:=ty_val(p);
- maxy:=miny;
- if y0<y1 then
- begin miny:=miny+y0; maxy:=maxy+y1; @+end
- else begin miny:=miny+y1; maxy:=maxy+y0; @+end;
- if x1<0 then miny:=miny+x1 @+else maxy:=maxy+x1;
- adjust_bbox(h);
- end;
-
-@ This case involves a recursive call that advances |bblast(h)| to the node of
-type |stop_clip_code| that matches |p|.
-
-@<Other cases for updating the bounding box...@>=
-start_clip_code: begin path_bbox(path_p(p));@/
- x0:=minx; y0:=miny;
- x1:=maxx; y1:=maxy;@/
- sminx:=minx_val(h); sminy:=miny_val(h);
- smaxx:=maxx_val(h); smaxy:=maxy_val(h);@/
- @<Reinitialize the bounding box in header |h| and call |set_bbox| recursively
- starting at |link(p)|@>;
- @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,
- |y0|, |y1|@>;
- minx:=sminx; miny:=sminy;
- maxx:=smaxx; maxy:=smaxy;
- adjust_bbox(h);
- end;
-
-@ @<Reinitialize the bounding box in header |h| and call |set_bbox|...@>=
-minx_val(h):=el_gordo;
-miny_val(h):=el_gordo;
-maxx_val(h):=-el_gordo;
-maxy_val(h):=-el_gordo;@/
-set_bbox(h,false)
-
-@ @<Clip the bounding box in |h| to the rectangle given by |x0|, |x1|,...@>=
-if minx_val(h)<x0 then minx_val(h):=x0;
-if miny_val(h)<y0 then miny_val(h):=y0;
-if maxx_val(h)>x1 then maxx_val(h):=x1;
-if maxy_val(h)>y1 then maxy_val(h):=y1
-
-@* \[22] Finding an envelope.
-When \MP\ has a path and a polygonal pen, it needs to express the desired
-shape in terms of things \ps\ can understand. The present task is to compute
-a new path that describes the region to be filled. It is convenient to
-define this as a two step process where the first step is determining what
-offset to use for each segment of the path.
-
-@ Given a pointer |c| to a cyclic path,
-and a pointer~|h| to the first knot of a pen polygon,
-the |offset_prep| routine changes the path into cubics that are
-associated with particular pen offsets. Thus if the cubic between |p|
-and~|q| is associated with the |k|th offset and the cubic between |q| and~|r|
-has offset |l| then |info(q)=zero_off+l-k|. (The constant |zero_off| is added
-to because |l-k| could be negative.)
-
-After overwriting the type information with offset differences, we no longer
-have a true path so we refer to the knot list returned by |offset_prep| as an
-``envelope spec.''
-@!@^envelope spec@>
-Since an envelope spec only determines relative changes in pen offsets,
-|offset_prep| sets a global variable |spec_offset| to the relative change from
-|h| to the first offset.
-
-@d zero_off=16384 {added to offset changes to make them positive}
-
-@<Glob...@>=
-spec_offset:integer; {number of pen edges between |h| and the initial offset}
-
-@ @p @t\4@>@<Declare subroutines needed by |offset_prep|@>@;
-function offset_prep(@!c,@!h:pointer):pointer;
-label not_found;
-var @!n:halfword; {the number of vertices in the pen polygon}
-@!p,@!q,@!q0,@!c0,@!r,@!w,@!ww:pointer; {for list manipulation}
-@!k_needed:integer; {amount to be added to |info(p)| when it is computed}
-@!w0:pointer; {a pointer to pen offset to use just before |p|}
-@!dxin,@!dyin:scaled; {the direction into knot |p|}
-@!turn_amt:integer; {change in pen offsets for the current cubic}
-@<Other local variables for |offset_prep|@>@;
-begin @<Initialize the pen size~|n|@>;
-@<Initialize the incoming direction and pen offset at |c|@>;
-p:=c; c0:=c; k_needed:=0;
-repeat 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@>;
-until q=c;
-@<Fix the offset change in |info(c)| and set the return value of
- |offset_prep|@>;
-end;
-
-@ We shall want to keep track of where certain knots on the cyclic path
-wind up in the envelope spec. It doesn't suffice just to keep pointers to
-knot nodes because some nodes are deleted while removing dead cubics. Thus
-|offset_prep| updates the following pointers
-
-@<Glob...@>=
-@!spec_p1,@!spec_p2:pointer; {pointers to distinguished knots}
-
-@ @<Set init...@>=
-spec_p1:=null; spec_p2:=null;
-
-@ @<Initialize the pen size~|n|@>=
-n:=0; p:=h;
-repeat incr(n);
-p:=link(p);
-until p=h
-
-@ Since the true incoming direction isn't known yet, we just pick a direction
-consistent with the pen offset~|h|. If this is wrong, it can be corrected
-later.
-
-@<Initialize the incoming direction and pen offset at |c|@>=
-dxin:=x_coord(link(h))-x_coord(knil(h));
-dyin:=y_coord(link(h))-y_coord(knil(h));
-if (dxin=0)and(dyin=0) then
- begin dxin:=y_coord(knil(h))-y_coord(h);
- dyin:=x_coord(h)-x_coord(knil(h));
- end;
-w0:=h
-
-@ We must be careful not to remove the only cubic in a cycle.
-
-But we must also be careful for another reason. If the user-supplied
-path starts with a set of degenerate cubics, the target node |q| can
-be collapsed to the initial node |p| which might be the same as the
-initial node |c| of the curve. This would cause the |offset_prep| routine
-to bail out too early, causing distress later on. (See for example
-the testcase reported by Bogus\l{}aw Jackowski in tracker id 267, case 52c
-on Sarovar.)
-
-@<Advance |p| to node |q|, removing any ``dead'' cubics...@>=
-q0:=q;
-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
- if r<>p then
- @<Remove the cubic following |p| and update the data structures
- to merge |r| into |p|@>;
-p:=r;
-until p=q;
-{ Check if we removed too much }
-if (q<>q0) and ((q<>c) or (c=c0)) then q:=link(q)
-
-@ @<Remove the cubic following |p| and update the data structures...@>=
-begin k_needed:=info(p)-zero_off;
-if r=q then q:=p
-else begin info(p):=k_needed+info(r);
- k_needed:=0;
- end;
-if r=c then
- begin info(p):=info(c); c:=p;
- end;
-if r=spec_p1 then spec_p1:=p;
-if r=spec_p2 then spec_p2:=p;
-r:=p; remove_cubic(p);
-end
-
-@ Not setting the |info| field of the newly created knot allows the splitting
-routine to work for paths.
-
-@<Declare subroutines needed by |offset_prep|@>=
-procedure split_cubic(@!p:pointer;@!t:fraction); {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;@/
-originator(r):=program_code;@/
-left_type(r):=explicit; right_type(r):=explicit;@#
-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))(x_coord(q));
-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))(y_coord(q));
-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;
-
-@ This does not set |info(p)| or |right_type(p)|.
-
-@<Declare subroutines needed by |offset_prep|@>=
-procedure remove_cubic(@!p:pointer); {removes the dead cubic following~|p|}
-var @!q:pointer; {the node that disappears}
-begin q:=link(p); link(p):=link(q);@/
-right_x(p):=right_x(q); right_y(p):=right_y(q);@/
-free_node(q,knot_node_size);
-end;
-
-@ Let $d\prec d'$ mean that the counter-clockwise angle from $d$ to~$d'$ is
-strictly between zero and $180^\circ$. Then we can define $d\preceq d'$ to
-mean that the angle could be zero or $180^\circ$. If $w_k=(u_k,v_k)$ is the
-$k$th pen offset, the $k$th pen edge direction is defined by the formula
-$$d_k=(u\k-u_k,\,v\k-v_k).$$
-When listed by increasing $k$, these directions occur in counter-clockwise
-order so that $d_k\preceq d\k$ for all~$k$.
-The goal of |offset_prep| is to find an offset index~|k| to associate with
-each cubic, such that the direction $d(t)$ of the cubic satisfies
-$$d_{k-1}\preceq d(t)\preceq d_k\qquad\hbox{for $0\le t\le 1$.}\eqno(*)$$
-We may have to split a cubic into many pieces before each
-piece corresponds to a unique offset.
-
-@<Split the cubic between |p| and |q|, if necessary, into cubics...@>=
-info(p):=zero_off+k_needed;
-k_needed:=0;@/
-@<Prepare for derivative computations;
- |goto not_found| if the current cubic is dead@>;
-@<Find the initial direction |(dx,dy)|@>;
-@<Update |info(p)| and find the offset $w_k$ such that
- $d_{k-1}\preceq(\\{dx},\\{dy})\prec d_k$; also advance |w0| for
- the direction change at |p|@>;
-@<Find the final direction |(dxin,dyin)|@>;
-@<Decide on the net change in pen offsets and set |turn_amt|@>;
-@<Complete the offset splitting process@>;@/
-w0:=pen_walk(w0,turn_amt);
-not_found: do_nothing
-
-@ @<Declare subroutines needed by |offset_prep|@>=
-function pen_walk(@!w:pointer;@!k:integer):pointer;
- {walk |k| steps around a pen from |w|}
-begin while k>0 do begin w:=link(w); decr(k); @+end;
-while k<0 do begin w:=knil(w); incr(k); @+end;
-pen_walk:=w;
-end;
-
-@ The direction 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 directions 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)$.
-
-@<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 directions of the pen and the curve}
-@!dx0,@!dy0:integer; {initial direction for the first cubic in 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; {a temporary value}
-
-@ @<Prepare for derivative computations...@>=
-dx0:=0; dy0:=0;
-x0:=right_x(p)-x_coord(p);
-x2:=x_coord(q)-left_x(q);
-x1:=left_x(q)-right_x(p);
-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);
-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)~$d(t)\succeq d_{k-1}$ for all~$t$
-and $d(0)\prec d_k$, or (ii)~$d(t)\preceq d_k$ for all~$t$ and
-$d(0)\succ d_{k-1}$.
-Then, in a sense, we're halfway done, since one of the two relations
-in $(*)$ is satisfied, and the other couldn't be satisfied for
-any other value of~|k|.
-
-Actually, the conditions can be relaxed somewhat since a relation such as
-$d(t)\succeq d_{k-1}$ restricts $d(t)$ to a half plane when all that really
-matters is whether $d(t)$ crosses the ray in the $d_{k-1}$ direction from
-the origin. The condition for case~(i) becomes $d_{k-1}\preceq d(0)\prec d_k$
-and $d(t)$ never crosses the $d_{k-1}$ ray in the clockwise direction.
-Case~(ii) is similar except $d(t)$ cannot cross the $d_k$ ray in the
-counterclockwise direction.
-
-The |fin_offset_prep| subroutine solves the stated subproblem.
-It has a parameter called |rise| that is |1| in
-case~(i), |-1| in case~(ii). Parameters |x0| through |y2| represent
-the derivative of the cubic following |p|.
-The |w| parameter should point to offset~$w_k$ and |info(p)| should already
-be set properly. The |turn_amt| parameter gives the absolute value of the
-overall net change in pen offsets.
-
-@<Declare subroutines needed by |offset_prep|@>=
-procedure fin_offset_prep(@!p:pointer;@!w:pointer;
- @!x0,@!x1,@!x2,@!y0,@!y1,@!y2:integer;@!rise,@!turn_amt: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|}
-@!q:pointer; {original |link(p)|}
-begin q:=link(p);
-loop @+begin if rise>0 then ww:=link(w) {a pointer to $w\k$}
- else ww:=knil(w); {a pointer to $w_{k-1}$}
- @<Compute test coefficients |(t0,t1,t2)|
- for $d(t)$ versus $d_k$ or $d_{k-1}$@>;
- t:=crossing_point(t0,t1,t2);
- if t>=fraction_one then
- if turn_amt>0 then t:=fraction_one @+else return;
- @<Split the cubic at $t$,
- and split off another cubic if the derivative crosses back@>;
- w:=ww;
- end;
-exit:end;
-
-@ We want $B(\\{t0},\\{t1},\\{t2};t)$ to be the dot product of $d(t)$ with a
-$-90^\circ$ rotation of the vector from |w| to |ww|. This makes the resulting
-function cross from positive to negative when $d_{k-1}\preceq d(t)\preceq d_k$
-begins to fail.
-
-@<Compute test coefficients |(t0,t1,t2)| for $d(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;
- if du<0 then begin negate(t0); negate(t1); negate(t2); @+end
- 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);
- if dv<0 then begin negate(t0); negate(t1); negate(t2); @+end
- end;
-if t0<0 then t0:=0 {should be positive without rounding error}
-
-@ The curve has crossed $d_k$ or $d_{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_cubic(p,t); p:=link(p); info(p):=zero_off+rise;
-decr(turn_amt);@/
-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);@/
-if turn_amt<0 then
- begin 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 t:=fraction_one;
- incr(turn_amt);
- if (t=fraction_one)and(link(p)<>q) then
- info(link(p)):=info(link(p))-rise
- else begin split_cubic(p,t); info(link(p)):=zero_off-rise;@/
- 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;
-end
-
-@ Now we must consider the general problem of |offset_prep|, when
-nothing is known about a given cubic. We start by finding its
-direction in the vicinity of |t=0|.
-
-If $z'(t)=0$, the given cubic is numerically unstable but |offset_prep|
-has not yet introduced any more numerical errors. Thus we can compute
-the true initial direction for the given cubic, even if it is almost
-degenerate.
-
-@<Find the initial direction |(dx,dy)|@>=
-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;
-if p=c then begin dx0:=dx; dy0:=dy; @+end
-
-@ @<Find the final direction |(dxin,dyin)|@>=
-dxin:=x2; dyin:=y2;
-if dxin=0 then if dyin=0 then
- begin dxin:=x1; dyin:=y1;
- if dxin=0 then if dyin=0 then
- begin dxin:=x0; dyin:=y0;
- end;
- end
-
-@ The next step is to bracket the initial direction between consecutive
-edges of the pen polygon. We must be careful to turn clockwise only if
-this makes the turn less than $180^\circ$. (A $180^\circ$ turn must be
-counter-clockwise in order to make \&{doublepath} envelopes come out
-@:double_path_}{\&{doublepath} primitive@>
-right.) This code depends on |w0| being the offset for |(dxin,dyin)|.
-
-@<Update |info(p)| and find the offset $w_k$ such that...@>=
-turn_amt:=get_turn_amt(w0, dx, dy, ab_vs_cd(dy,dxin,dx,dyin)>=0);
-w:=pen_walk(w0, turn_amt);
-w0:=w;
-info(p):=info(p)+turn_amt
-
-@ Decide how many pen offsets to go away from |w| in order to find the offset
-for |(dx,dy)|, going counterclockwise if |ccw| is |true|. This assumes that
-|w| is the offset for some direction $(x',y')$ from which the angle to |(dx,dy)|
-in the sense determined by |ccw| is less than or equal to $180^\circ$.
-
-If the pen polygon has only two edges, they could both be parallel
-to |(dx,dy)|. In this case, we must be careful to stop after crossing the first
-such edge in order to avoid an infinite loop.
-
-@<Declare subroutines needed by |offset_prep|@>=
-function get_turn_amt(@!w:pointer; @!dx,@!dy:scaled; ccw:boolean):integer;
-label done;
-var @!ww:pointer; {a neighbor of knot~|w|}
-@!s:integer; {turn amount so far}
-@!t:integer; {|ab_vs_cd| result}
-begin s:=0;
-if ccw then
- begin ww:=link(w);
- repeat t:=ab_vs_cd(dy,x_coord(ww)-x_coord(w),@| dx,y_coord(ww)-y_coord(w));
- if t<0 then goto done;
- incr(s);
- w:=ww; ww:=link(ww);
- until t<=0;
- done: end
-else begin ww:=knil(w);
- while ab_vs_cd(dy,x_coord(w)-x_coord(ww),@|
- dx,y_coord(w)-y_coord(ww))<0 do
- begin decr(s);
- w:=ww; ww:=knil(ww);
- end;
- end;
-get_turn_amt:=s;
-end;
-
-@ When we're all done, the final offset is |w0| and the final curve direction
-is |(dxin,dyin)|. With this knowledge of the incoming direction at |c|, we
-can correct |info(c)| which was erroneously based on an incoming offset
-of~|h|.
-
-@d fix_by(#)==info(c):=info(c)+#
-
-@<Fix the offset change in |info(c)| and set the return value of...@>=
-spec_offset:=info(c)-zero_off;
-if link(c)=c then info(c):=zero_off+n
-else begin fix_by(k_needed);
- while w0<>h do
- begin fix_by(1); w0:=link(w0); @+end;
- while info(c)<=zero_off-n do fix_by(n);
- while info(c)>zero_off do fix_by(-n);
- if (info(c)<>zero_off)and(ab_vs_cd(dy0,dxin,dx0,dyin)>=0) then fix_by(n);
- end;
-offset_prep:=c
-
-@ Finally we want to reduce the general problem to situations that
-|fin_offset_prep| can handle. We split the cubic into at most three parts
-with respect to $d_{k-1}$, and apply |fin_offset_prep| to each part.
-
-@<Complete the offset splitting process@>=
-ww:=knil(w);
-@<Compute test coeff...@>;
-@<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set
- |t:=fraction_one+1|@>;
-if t>fraction_one then
- fin_offset_prep(p,w,x0,x1,x2,y0,y1,y2,1,turn_amt)
-else begin split_cubic(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,w,x0,x1a,x2a,y0,y1a,y2a,1,0); x0:=x2a; y0:=y2a;
- info(r):=zero_off-1;
- if turn_amt>=0 then
- begin t1:=t_of_the_way(t1)(t2);
- if t1>0 then t1:=0;
- t:=crossing_point(0,-t1,-t2);
- if t>fraction_one then t:=fraction_one;
- @<Split off another rising cubic for |fin_offset_prep|@>;
- fin_offset_prep(r,ww,x0,x1,x2,y0,y1,y2,-1,0);
- end
- else fin_offset_prep(r,ww,x0,x1,x2,y0,y1,y2,-1,-1-turn_amt);
- end
-
-@ @<Split off another rising cubic for |fin_offset_prep|@>=
-split_cubic(r,t); info(link(r)):=zero_off+1;@/
-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),w,x0a,x1a,x2,y0a,y1a,y2,1,turn_amt);
-x2:=x0a; y2:=y0a
-
-@ At this point, the direction of the incoming pen edge is |(-du,-dv)|.
-When the component of $d(t)$ perpendicular to |(-du,-dv)| crosses zero, we
-need to decide whether the directions are parallel or antiparallel. We
-can test this by finding the dot product of $d(t)$ and |(-du,-dv)|, but this
-should be avoided when the value of |turn_amt| already determines the
-answer. If |t2<0|, there is one crossing and it is antiparallel only if
-|turn_amt>=0|. If |turn_amt<0|, there should always be at least one
-crossing and the first crossing cannot be antiparallel.
-
-@<Find the first |t| where $d(t)$ crosses $d_{k-1}$ or set...@>=
-t:=crossing_point(t0,t1,t2);
-if turn_amt>=0 then
- if t2<0 then t:=fraction_one+1
- else begin u0:=t_of_the_way(x0)(x1);
- u1:=t_of_the_way(x1)(x2);
- ss:=take_fraction(-du,t_of_the_way(u0)(u1));@/
- v0:=t_of_the_way(y0)(y1);
- v1:=t_of_the_way(y1)(y2);
- ss:=ss+take_fraction(-dv,t_of_the_way(v0)(v1));@/
- if ss<0 then t:=fraction_one+1;
- end
-else if t>fraction_one then t:=fraction_one;
-
-@ @<Other local variables for |offset_prep|@>=
-@!u0,@!u1,@!v0,@!v1:integer; {intermediate values for $d(t)$ calculation}
-@!ss:integer; {the part of the dot product computed so far}
-@!d_sign:-1..1; {sign of overall change in direction for this cubic}
-
-@ If the cubic almost has a cusp, it is a numerically ill-conditioned
-problem to decide which way it loops around but that's OK as long we're
-consistent. To make \&{doublepath} envelopes work properly, reversing
-the path should always change the sign of |turn_amt|.
-
-@<Decide on the net change in pen offsets and set |turn_amt|@>=
-d_sign:=ab_vs_cd(dx,dyin, dxin,dy);
-if d_sign=0 then
- @<Check rotation direction based on node position@>;
-if d_sign=0 then
- if dx=0 then
- if dy>0 then d_sign:=1 @+else d_sign:=-1
- else if dx>0 then d_sign:=1 @+else d_sign:=-1;
-@<Make |ss| negative if and only if the total change in direction is
- more than $180^\circ$@>;
-turn_amt:=get_turn_amt(w, dxin, dyin, d_sign>0);
-if ss<0 then turn_amt:=turn_amt-d_sign*n
-
-@ We check rotation direction by looking at the vector connecting the current
-node with the next. If its angle with incoming and outgoing tangents has the
-same sign, we pick this as |d_sign|, since it means we have a flex, not a cusp.
-Otherwise we proceed to the cusp code.
-
-@<Check rotation direction based on node position@>=
-begin
-u0:=x_coord(q)-x_coord(p);
-u1:=y_coord(q)-y_coord(p);
-d_sign:=half(ab_vs_cd(dx, u1, u0, dy)+ab_vs_cd(u0, dyin, dxin, u1));
-end
-
-
-@ In order to be invariant under path reversal, the result of this computation
-should not change when |x0|, |y0|, $\ldots$ are all negated and |(x0,y0)| is
-then swapped with |(x2,y2)|. We make use of the identities
-|take_fraction(-a,-b)=take_fraction(a,b)| and
-|t_of_the_way(-a)(-b)=-(t_of_the_way(a)(b))|.
-
-@<Make |ss| negative if and only if the total change in direction is...@>=
-t0:=half(take_fraction(x0,y2))-half(take_fraction(x2,y0));@/
-t1:=half(take_fraction(x1,y0+y2))-half(take_fraction(y1,x0+x2));@/
-if t0=0 then t0:=d_sign; {path reversal always negates |d_sign|}
-if t0>0 then
- begin t:=crossing_point(t0,t1,-t0);
- u0:=t_of_the_way(x0)(x1);
- u1:=t_of_the_way(x1)(x2);@/
- v0:=t_of_the_way(y0)(y1);
- v1:=t_of_the_way(y1)(y2);
- end
-else begin t:=crossing_point(-t0,t1,t0);
- u0:=t_of_the_way(x2)(x1);
- u1:=t_of_the_way(x1)(x0);@/
- v0:=t_of_the_way(y2)(y1);
- v1:=t_of_the_way(y1)(y0);
- end;
-ss:=take_fraction(x0+x2,t_of_the_way(u0)(u1))+@|
- take_fraction(y0+y2,t_of_the_way(v0)(v1))
-
-@ Here's a routine that prints an envelope spec in symbolic form. It assumes
-that the |cur_pen| has not been walked around to the first offset.
-
-@p procedure print_spec(@!cur_spec,@!cur_pen:pointer;@!s:str_number);
-var @!p,@!q:pointer; {list traversal}
-@!w:pointer; {the current pen offset}
-begin print_diagnostic("Envelope spec",s,true);
-p:=cur_spec; w:=pen_walk(cur_pen,spec_offset);
-print_ln;@/
-print_two(x_coord(cur_spec),y_coord(cur_spec));
-print(" % beginning with offset ");
-print_two(x_coord(w),y_coord(w));
-repeat
- repeat q:=link(p);
- @<Print the cubic between |p| and |q|@>;
- p:=q;
- until (p=cur_spec) or (info(p)<>zero_off);
- if info(p)<>zero_off then
- @<Update |w| as indicated by |info(p)| and print an explanation@>;
-until p=cur_spec;
-print_nl(" & cycle");
-end_diagnostic(true);
-end;
-
-@ @<Update |w| as indicated by |info(p)| and print an explanation@>=
-begin w:=pen_walk(w,info(p)-zero_off);
-print(" % ");
-if info(p)>zero_off then print("counter");
-print("clockwise to offset ");
-print_two(x_coord(w),y_coord(w));
-end
-
-@ @<Print the cubic between |p| and |q|@>=
-begin print_nl(" ..controls ");
-print_two(right_x(p),right_y(p));
-print(" and ");
-print_two(left_x(q),left_y(q));
-print_nl(" ..");
-print_two(x_coord(q),y_coord(q));
-end
-
-@ Once we have an envelope spec, the remaining task to construct the actual
-envelope by offsetting each cubic as determined by the |info| fields in
-the knots. First we use |offset_prep| to convert the |c| into an envelope
-spec. Then we add the offsets so that |c| becomes a cyclic path that represents
-the envelope.
-
-The |ljoin| and |miterlim| parameters control the treatment of points where the
-pen offset changes, and |lcap| controls the endpoints of a \&{doublepath}.
-The endpoints are easily located because |c| is given in undoubled form
-and then doubled in this procedure. We use |spec_p1| and |spec_p2| to keep
-track of the endpoints and treat them like very sharp corners.
-Butt end caps are treated like beveled joins; round end caps are treated like
-round joins; and square end caps are achieved by setting |join_type:=3|.
-
-None of these parameters apply to inside joins where the convolution tracing
-has retrograde lines. In such cases we use a simple connect-the-endpoints
-approach that is achieved by setting |join_type:=2|.
-
-@p @t\4@>@<Declare a function called |insert_knot|@>@;
-function make_envelope(@!c,@!h:pointer;@!ljoin,@!lcap:small_number;
- @!miterlim:scaled):pointer;
-label done;
-var @!p,@!q,@!r,@!q0:pointer; {for manipulating the path}
-@!join_type:0..3; {codes |0..3| for mitered, round, beveled, or square}
-@!w,@!w0:pointer; {the pen knot for the current offset}
-@!qx,@!qy:scaled; {unshifted coordinates of |q|}
-@!k,@!k0:halfword; {controls pen edge insertion}
-@<Other local variables for |make_envelope|@>@;
-begin spec_p1:=null; spec_p2:=null;
-if left_type(c)=endpoint then
- @<Double the path |c|, and set |spec_p1| and |spec_p2|@>;
-@<Use |offset_prep| to compute the envelope spec then walk |h| around to
- the initial offset@>;
-w:=h;
-p:=c;
-repeat q:=link(p); q0:=q;
-qx:=x_coord(q); qy:=y_coord(q);
-k:=info(q);@/
-k0:=k; w0:=w;
-dxin:=0;
-if k<>zero_off then
- @<Set |join_type| to indicate how to handle offset changes at~|q|@>;
-@<Add offset |w| to the cubic from |p| to |q|@>;
-while k<>zero_off do
- begin @<Step |w| and move |k| one step closer to |zero_off|@>;
- if (join_type=1)or(k=zero_off) then
- q:=insert_knot(q,qx+x_coord(w),qy+y_coord(w));
- end;
-if q<>link(p) then @<Set |p=link(p)| and add knots between |p| and |q| as
- requred by |join_type|@>;
-p:=q;
-until q0=c;
-make_envelope:=c;
-end;
-
-@ @<Use |offset_prep| to compute the envelope spec then walk |h| around to...@>=
-c:=offset_prep(c,h);
-if internal[tracing_specs]>0 then print_spec(c,h,"");
-h:=pen_walk(h,spec_offset)
-
-@ Mitered and squared-off joins depend on path directions that are difficult to
-compute for degenerate cubics. The envelope spec computed by |offset_prep| can
-have degenerate cubics only if the entire cycle collapses to a single
-degenerate cubic. Setting |join_type:=2| in this case makes the computed
-envelope degenerate as well.
-
-@<Set |join_type| to indicate how to handle offset changes at~|q|@>=
-dxin:=0; dyin:=0; dxout:=0; dyout:=0; join_type:=0;
-if k<zero_off then join_type:=2
-else begin if (q<>spec_p1)and(q<>spec_p2) then join_type:=ljoin
- else if lcap=2 then join_type:=3
- else join_type:=2-lcap;
- if (join_type=0)or(join_type=3) then
- begin @<Set the incoming and outgoing directions at |q|; in case of
- degeneracy set |join_type:=2|@>;
- if join_type=0 then
- @<If |miterlim| is less than the secant of half the angle at |q|
- then set |join_type:=2|@>;
- end;
- end
-
-@ @<If |miterlim| is less than the secant of half the angle at |q|...@>=
-begin tmp:=take_fraction(miterlim,fraction_half+@|
- half(take_fraction(dxin,dxout)+take_fraction(dyin,dyout)));
-if tmp<unity then
- if take_scaled(miterlim,tmp)<unity then join_type:=2;
-end
-
-@ @<Other local variables for |make_envelope|@>=
-@!dxin,@!dyin,@!dxout,@!dyout:fraction;
- {directions at |q| when square or mitered}
-@!tmp:scaled; {a temporary value}
-
-@ The coordinates of |p| have already been shifted unless |p| is the first
-knot in which case they get shifted at the very end.
-
-@<Add offset |w| to the cubic from |p| to |q|@>=
-right_x(p):=right_x(p)+x_coord(w);
-right_y(p):=right_y(p)+y_coord(w);@/
-left_x(q):=left_x(q)+x_coord(w);
-left_y(q):=left_y(q)+y_coord(w);@/
-x_coord(q):=x_coord(q)+x_coord(w);
-y_coord(q):=y_coord(q)+y_coord(w);@/
-left_type(q):=explicit;
-right_type(q):=explicit
-
-@ @<Step |w| and move |k| one step closer to |zero_off|@>=
-if k>zero_off then
- begin w:=link(w); decr(k); @+end
-else begin w:=knil(w); incr(k); @+end
-
-@ The cubic from |q| to the new knot at |(x,y)| becomes a line segment and
-the |right_x| and |right_y| fields of |r| are set from |q|. This is done in
-case the cubic containing these control points is ``yet to be examined.''
-
-@<Declare a function called |insert_knot|@>=
-function insert_knot(@!q:pointer;@!x,@!y:scaled):pointer;
- {returns the inserted knot}
-var @!r:pointer; {the new knot}
-begin r:=get_node(knot_node_size);
-link(r):=link(q); link(q):=r;@/
-right_x(r):=right_x(q);
-right_y(r):=right_y(q);@/
-x_coord(r):=x;
-y_coord(r):=y;@/
-right_x(q):=x_coord(q);
-right_y(q):=y_coord(q);@/
-left_x(r):=x_coord(r);
-left_y(r):=y_coord(r);@/
-left_type(r):=explicit;
-right_type(r):=explicit;
-originator(r):=program_code;@/
-insert_knot:=r;
-end;
-
-@ After setting |p:=link(p)|, either |join_type=1| or |q=link(p)|.
-
-@<Set |p=link(p)| and add knots between |p| and |q| as...@>=
-begin p:=link(p);
-if (join_type=0)or(join_type=3) then
- begin if join_type=0 then
- @<Insert a new knot |r| between |p| and |q| as required for a mitered join@>
- else @<Make |r| the last of two knots inserted between |p| and |q| to form a
- squared join@>;
- if r<>null then
- begin right_x(r):=x_coord(r);
- right_y(r):=y_coord(r);
- end;
- end;
-end
-
-@ For very small angles, adding a knot is unnecessary and would cause numerical
-problems, so we just set |r:=null| in that case.
-
-@<Insert a new knot |r| between |p| and |q| as required for a mitered join@>=
-begin det:=take_fraction(dyout,dxin)-take_fraction(dxout,dyin);
-if abs(det)<26844 then r:=null {sine $<10^{-4}$}
-else begin tmp:=take_fraction(x_coord(q)-x_coord(p),dyout)-@|
- take_fraction(y_coord(q)-y_coord(p),dxout);
- tmp:=make_fraction(tmp,det);
- r:=insert_knot(p,x_coord(p)+take_fraction(tmp,dxin),@|
- y_coord(p)+take_fraction(tmp,dyin));
- end;
-end
-
-@ @<Other local variables for |make_envelope|@>=
-@!det:fraction; {a determinant used for mitered join calculations}
-
-@ @<Make |r| the last of two knots inserted between |p| and |q| to form a...@>=
-begin ht_x:=y_coord(w)-y_coord(w0);
-ht_y:=x_coord(w0)-x_coord(w);
-while (abs(ht_x)<fraction_half)and(abs(ht_y)<fraction_half) do
- begin double(ht_x); double(ht_y);
- end;
-@<Scan the pen polygon between |w0| and |w| and make |max_ht| the range dot
- product with |(ht_x,ht_y)|@>;
-tmp:=make_fraction(max_ht,take_fraction(dxin,ht_x)+take_fraction(dyin,ht_y));
-r:=insert_knot(p,x_coord(p)+take_fraction(tmp,dxin),@|
- y_coord(p)+take_fraction(tmp,dyin));
-tmp:=make_fraction(max_ht,take_fraction(dxout,ht_x)+take_fraction(dyout,ht_y));
-r:=insert_knot(r,x_coord(q)+take_fraction(tmp,dxout),@|
- y_coord(q)+take_fraction(tmp,dyout));
-end
-
-@ @<Other local variables for |make_envelope|@>=
-@!ht_x,@!ht_y:fraction; {perpendicular to the segment from |p| to |q|}
-@!max_ht:scaled; {maximum height of the pen polygon above the |w0|-|w| line}
-@!kk:halfword; {keeps track of the pen vertices being scanned}
-@!ww:pointer; {the pen vertex being tested}
-
-@ The dot product of the vector from |w0| to |ww| with |(ht_x,ht_y)| ranges
-from zero to |max_ht|.
-
-@<Scan the pen polygon between |w0| and |w| and make |max_ht| the range...@>=
-max_ht:=0;
-kk:=zero_off;
-ww:=w;
-loop @+begin @<Step |ww| and move |kk| one step closer to |k0|@>;
- if kk=k0 then goto done;
- tmp:=take_fraction(x_coord(ww)-x_coord(w0),ht_x)+@|
- take_fraction(y_coord(ww)-y_coord(w0),ht_y);
- if tmp>max_ht then max_ht:=tmp;
- end;
-done:do_nothing
-
-@ @<Step |ww| and move |kk| one step closer to |k0|@>=
-if kk>k0 then
- begin ww:=link(ww); decr(kk); @+end
-else begin ww:=knil(ww); incr(kk); @+end
-
-@ @<Double the path |c|, and set |spec_p1| and |spec_p2|@>=
-begin spec_p1:=htap_ypoc(c);
-spec_p2:=path_tail;
-originator(spec_p1):=program_code;
-link(spec_p2):=link(spec_p1);
-link(spec_p1):=c;@/
-remove_cubic(spec_p1);
-c:=spec_p1;
-if c<>link(c) then begin
-originator(spec_p2):=program_code;
-remove_cubic(spec_p2);
-end
-else @<Make |c| look like a cycle of length one@>;
-end
-
-@ @<Make |c| look like a cycle of length one@>=
-begin left_type(c):=explicit; right_type(c):=explicit;
-left_x(c):=x_coord(c); left_y(c):=y_coord(c);
-right_x(c):=x_coord(c); right_y(c):=y_coord(c);
-end;
-
-@ In degenerate situations we might have to look at the knot preceding~|q|.
-That knot is |p| but if |p<>c|, its coordinates have already been offset by |w|.
-
-@<Set the incoming and outgoing directions at |q|; in case of...@>=
-dxin:=x_coord(q)-left_x(q);
-dyin:=y_coord(q)-left_y(q);
-if (dxin=0)and(dyin=0) then
- begin dxin:=x_coord(q)-right_x(p);
- dyin:=y_coord(q)-right_y(p);
- if (dxin=0)and(dyin=0) then
- begin dxin:=x_coord(q)-x_coord(p);
- dyin:=y_coord(q)-y_coord(p);
- if p<>c then {the coordinates of |p| have been offset by |w|}
- begin dxin:=dxin+x_coord(w);
- dyin:=dyin+y_coord(w);
- end;
- end;
- end;
-tmp:=pyth_add(dxin,dyin);
-if tmp=0 then join_type:=2
-else begin dxin:=make_fraction(dxin,tmp);
- dyin:=make_fraction(dyin,tmp);
- @<Set the outgoing direction at |q|@>;
- end
-
-@ If |q=c| then the coordinates of |r| and the control points between |q|
-and~|r| have already been offset by |h|.
-
-@<Set the outgoing direction at |q|@>=
-dxout:=right_x(q)-x_coord(q);
-dyout:=right_y(q)-y_coord(q);
-if (dxout=0)and(dyout=0) then
- begin r:=link(q);
- dxout:=left_x(r)-x_coord(q);
- dyout:=left_y(r)-y_coord(q);
- if (dxout=0)and(dyout=0) then
- begin dxout:=x_coord(r)-x_coord(q);
- dyout:=y_coord(r)-y_coord(q);
- end;
- end;
-if q=c then
- begin dxout:=dxout-x_coord(h);
- dyout:=dyout-y_coord(h);
- end;
-tmp:=pyth_add(dxout,dyout);
-if tmp=0 then confusion("degenerate spec");
-@:this can't happen degerate spec}{\quad degenerate spec@>
-dxout:=make_fraction(dxout,tmp);
-dyout:=make_fraction(dyout,tmp)
-
-@* \[23] 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
-|crossing_point|.\
-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. The |bisect_stack| array 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}
-
-@<Glob...@>=
-bisect_stack:array[0..bistack_size] of integer;
-bisect_ptr:0..bistack_size;
-
-@ @<Check the ``constant''...@>=
-if int_packets+17*int_increment>bistack_size then bad:=19;
-
-@ 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:=halfp(cur_t+1); cur_tt:=halfp(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:=halfp(cur_t); cur_tt:=halfp(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;
-
-@* \[24] Dynamic linear equations.
-\MP\ 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|.
-
-
-The next patch detects overflow of independent-variable serial
-numbers. Diagnosed and patched by Thorsten Dahlheimer.
-
-@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);
-@:METAPOST 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 $\alpha_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. \MP\ 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 \MP\ 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)>halfp(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 \MP\ 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);
- loop @+begin r:=info(p); q:=p; p:=link(q);
- if r=null then goto done3;
- if value(info(p))>=value(r) then
- begin print_nl("Out of order at "); print_int(p);
-@.Out of order...@>
- end;
- end;
-done3: do_nothing;
- end
-
-@* \[25] 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 \MP\ 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: value(q):=copy_pen(v);
-path_type: value(q):=copy_path(v);
-picture_type: begin value(q):=v; add_edge_ref(v);
- end;
-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
-
-@* \[26] Introduction to the syntactic routines.
-Let's pause a moment now and try to look at the Big Picture.
-The \MP\ 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 \MP\ that
-actually controls the activities.
-
-Our current goal is to come to grips with the |get_next| procedure,
-which is the keystone of \MP'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,
-\MP\ 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 \MP\ 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;
-
-@* \[27] Input stacks and states.
-The state of \MP'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 \MP\ 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. \MP\ 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 an ordinary text file. Special codes
-|is_term..max_spec_src| indicate other sources of input text.
-
-@d is_term=0 {|name| value when reading from the terminal for normal input}
-@d is_read=1 {|name| value when executing a \&{readstring} or \&{readfrom}}
-@d is_scantok=2 {|name| value when reading text generated by \&{scantokens}}
-@d max_spec_src=is_scantok
-
-@ 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 figs}', we will have |index=1| while reading
-the file \.{figs.mp}. 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 and some |index| values may correspond
-to \.{MPX} files that are not currently on the stack.
-
-The global variable |in_open| is equal to the highest |index| value counting
-\.{MPX} files but excluding token-list input levels. 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=is_term|,
-and |cur_file| as an abbreviation for |input_file[index]|.
-
-When \MP\ is not reading from the terminal, the global variable |line| contains
-the line number in the current file, for use in error messages. More precisely,
-|line| is a macro for |line_stack[index]| and the |line_stack| array gives
-the line number for each file in the |input_file| array.
-
-When an \.{MPX} file is opened the file name is stored in the |mpx_name|
-array so that the name doesn't get lost when the file is temporarily removed
-from the input stack.
-Thus when |input_file[k]| is an \.{MPX} file, its name is |mpx_name[k]|
-and it contains translated \TeX\ pictures for |input_file[k-1]|.
-Since this is not an \.{MPX} file, we have
-$$ \hbox{|mpx_name[k-1]<=absent|}. $$
-This |name| field is set to |finished| when |input_file[k]| is completely
-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|, that is really a macro for the current entry
-in `\ignorespaces|@!page_stack:array[0..max_in_open] of integer|\unskip'
-by analogy with |line_stack|.
-@^system dependencies@>
-
-@d terminal_input==(name=is_term) {are we reading from the terminal?}
-@d cur_file==input_file[index] {the current |alpha_file| variable}
-@d line==line_stack[index] {current line number in the current source file}
-@d in_name==iname_stack[index] {a string used to construct \.{MPX} file names}
-@d in_area==iarea_stack[index] {another string for naming \.{MPX} files}
-@d absent=1 {|name_field| value for unused |mpx_in_stack| entries}
-@d mpx_reading==(mpx_name[index]>absent)
- {when reading a file, is it an \.{MPX} file?}
-@d finished=0
- {|name_field| value when the corresponding \.{MPX} file is finished}
-
-@<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_stack : array[0..max_in_open] of integer; {the line number for each file}
-@!iname_stack : array[0..max_in_open] of str_number;
- {used for naming \.{MPX} files}
-@!iarea_stack : array[0..max_in_open] of str_number;
- {used for naming \.{MPX} files}
-@!mpx_name : array[0..max_in_open] of halfword;
-
-@ 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|}
-
-@ Notice that the |line| isn't valid when |token_state| is true because it
-depends on |index|. If we really need to know the line number for the
-topmost file in the index stack we use the following function. If a page
-number or other information is needed, this routine should be modified to
-compute it as well.
-@^system dependencies@>
-
-@<Declare a function called |true_line|@>=
-function true_line: integer;
-var @!k:0..stack_size; {an index into the input stack}
-begin if file_state and (name>max_spec_src) then true_line:=line
-else begin k:=input_ptr;
- while (k>0)and(input_stack[k].index_field>max_in_open)or@|
- (input_stack[k].name_field<=max_spec_src) do
- decr(k);
- true_line:=line_stack[k];
- end;
-end;
-
-@ 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 \MP'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>max_spec_src) 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>max_spec_src then
- begin print_nl("l."); print_int(true_line);
- end
-else if terminal_input then
- if file_ptr=0 then print_nl("<*>") @+else print_nl("<insert>")
-else if name=is_scantok then print_nl("<scantokens>")
-else print_nl("<read>");
-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 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.
-
-@d void==null+1 {a null pointer different from |null|}
-
-@<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 \MP'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
-
-@* \[28] 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);
-@:MetaPost 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 \MP\ 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);
-@:MetaPost capacity exceeded text input levels}{\quad text input levels@>
-if first=buf_size then overflow("buffer size",buf_size);
-@:MetaPost capacity exceeded buffer size}{\quad buffer size@>
-incr(in_open); push_input; index:=in_open;
-mpx_name[index]:=absent;
-start:=first;
-name:=is_term; {|terminal_input| is now |true|}
-end;
-
-@ Conversely, the variables must be downdated when such a level of input
-is finished. Any associated \.{MPX} file must also be closed and popped
-off the file stack.
-
-@p procedure end_file_reading;
-begin if in_open>index then
- if (mpx_name[in_open]=absent)or(name<=max_spec_src) then confusion("endinput")
-@:this can't happen endinput}{\quad endinput@>
- else begin a_close(input_file[in_open]); {close an \.{MPX} file}
- delete_str_ref(mpx_name[in_open]);
- decr(in_open);
- end;
-first:=start;
-if index<>in_open then confusion("endinput");
-if name>max_spec_src then
- begin a_close(cur_file);
- delete_str_ref(name);
- delete_str_ref(in_name); delete_str_ref(in_area);
- end;
-pop_input; decr(in_open);
-end;
-
-@ Here is a function that tries to resume input from an \.{MPX} file already
-associated with the current input file. It returns |false| if this doesn't
-work.
-
-@p function begin_mpx_reading:boolean;
-begin if in_open<>index+1 then begin_mpx_reading:=false
-else begin if mpx_name[in_open]<=absent then confusion("mpx");
-@:this can't happen mpx}{\quad mpx@>
- if first=buf_size then overflow("buffer size",buf_size);
-@:MetaPost capacity exceeded buffer size}{\quad buffer size@>
- push_input; index:=in_open;
- start:=first;
- name:=mpx_name[in_open]; add_str_ref(name);
- @<Put an empty line in the input buffer@>;
- begin_mpx_reading:=true;
- end;
-end;
-
-@ This procedure temporarily stops reading an \.{MPX} file.
-
-@p procedure end_mpx_reading;
-begin if in_open<>index then confusion("mpx");
-@:this can't happen mpx}{\quad mpx@>
-if loc<limit then
- @<Complain that we are not at the end of a line in the \.{MPX} file@>;
-first:=start;
-pop_input;
-end;
-
-@ Here we enforce a restriction that simplifies the input stacks considerably.
-This should not inconvenience the user because \.{MPX} files are generated
-by an auxiliary program called \.{DVItoMP}.
-
-@ @<Complain that we are not at the end of a line in the \.{MPX} file@>=
-begin print_err("`mpxbreak' must be at the end of a line");
-help4("This file contains picture expressions for btex...etex")@/
- ("blocks. Such files are normally generated automatically")@/
- ("but this one seems to be messed up. I'm going to ignore")@/
- ("the rest of this line.");@/
-error;
-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 \MP'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:=is_term;
-mpx_name[0]:=absent;
-force_eof:=false;
-if not init_terminal then goto final_end;
-limit:=last; first:=last+1; {|init_terminal| has set |loc| and |last|}
-end;
-
-@* \[29] Getting the next token.
-The heart of \MP'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 \MP's
-eyes and mouth, reading the source files and gobbling them up. And it also
-helps \MP\ 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
-\MP\ 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}
-@d tex_flushing=7 {|scanner_status| when skipping \TeX\ material}
-
-@<Glob...@>=
-@!scanner_status:normal..tex_flushing; {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 if scanner_status=tex_flushing then
- @<Check if the file has ended while flushing \TeX\ material and set the
- result value for |check_outer_validity|@>
-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;
-
-@ @<Check if the file has ended while flushing \TeX\ material and set...@>=
-if cur_sym<>0 then check_outer_validity:=true
-else begin deletions_allowed:=false;
- print_err("TeX mode didn't end; all text was ignored after line ");
- print_int(warning_info);
- help2("The file ended while I was looking for the `etex' to")@/
- ("finish this TeX material. I've inserted `etex' now.");@/
- cur_sym := frozen_etex;
- ins_error;@/
- deletions_allowed:=true; check_outer_validity:=false;
- 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 print(text(warning_info))
- else print_variable_name(warning_info);
- cur_sym:=frozen_end_def;
- end;
-loop_defining: begin print("the text of a "); 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 \MP\ 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.
-Note that the behavior depends on the |scanner_status| because percent signs
-and double quotes need to be passed over when skipping TeX material.
-
-@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}
- common_ending, {go here to finish getting a symbolic token}
- 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@>;
-common_ending: @<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 if scanner_status=tex_flushing then
- if loc<limit then goto switch;
- @<Move to next line of file, or |goto restart| if there is no next line@>;
- check_interrupt;
- goto switch;
- end;
-string_class: if scanner_status=tex_flushing then goto switch
- else @<Get a string token and |return|@>;
-isolated_classes: begin k:=loc-1; goto found;
- end;
-invalid_class: if scanner_status=tex_flushing then goto switch
- else @<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 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<32768 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<32768 then @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably
- large@>
-else if scanner_status<>tex_flushing then
- begin print_err("Enormous number has been reduced");
-@.Enormous number...@>
- help2("I can't handle numbers bigger than 32767.99998;")@/
- ("so I've changed your constant to that maximum amount.");@/
- deletions_allowed:=false; error; deletions_allowed:=true;
- cur_mod:=el_gordo;
- end;
-cur_cmd:=numeric_token; return
-
-@ @<Set |cur_mod:=n*unity+f| and check if it is uncomfortably large@>=
-begin cur_mod:=n*unity+f;
-if cur_mod>=fraction_one then
- if (internal[warning_check]>0) and (scanner_status<>tex_flushing) then
- begin print_err("Number is too large (");
- print_scaled(cur_mod);
- print_char(")");
- help3("It is at least 4096. 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
-
-@ 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>max_spec_src 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?}
-
-@ We must decrement |loc| in order to leave the buffer in a valid state
-when an error condition causes us to |goto restart| without calling
-|end_file_reading|.
-
-@<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 force_eof:=false;
- decr(loc);
- if mpx_reading then
- @<Complain that the \.{MPX} file ended unexpectly; then set
- |cur_sym:=frozen_mpx_break| and |goto comon_ending|@>
- else begin print_char(")"); decr(open_parens);
- update_terminal; {show user that file has been read}
- end_file_reading; {resume previous level}
- if check_outer_validity then goto restart @+else goto restart;
- end
- end;
-buffer[limit]:="%"; first:=limit+1; loc:=start; {ready to read}
-end
-
-@ We should never actually come to the end of an \.{MPX} file because such
-files should have an \&{mpxbreak} after the translation of the last
-\&{btex}$\,\ldots\,$\&{etex} block.
-
-@<Complain that the \.{MPX} file ended unexpectly; then set...@>=
-begin mpx_name[index]:=finished;
-print_err("mpx file ended unexpectedly");
-help4("The file had too few picture expressions for btex...etex")@/
- ("blocks. Such files are normally generated automatically")@/
- ("but this one got messed up. You might want to insert a")@/
- ("picture expression now.");@/
-deletions_allowed:=false; error; deletions_allowed:=true;
-cur_sym:=frozen_mpx_break; goto common_ending;
-end
-
-@ Sometimes we want to make it look as though we have just read a blank line
-without really doing so.
-
-@<Put an empty line in the input buffer@>=
-last:=first; limit:=last; {simulate |input_ln| and |firm_up_the_line|}
-buffer[limit]:="%"; first:=limit+1; loc:=start
-
-@ 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 `\.{=>}'.
-\MP\ 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;
-
-@* \[30] Dealing with \TeX\ material.
-The \&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}$\,\ldots\,$\&{etex}
-features need to be implemented at a low level in the scanning process
-so that \MP\ can stay in synch with the a preprocessor that treats
-blocks of \TeX\ material as they occur in the input file without trying
-to expand \MP\ macros. Thus we need a special version of |get_next|
-that does not expand macros and such but does handle \&{btex},
-\&{verbatimtex}, etc.
-
-The special version of |get_next| is called |get_t_next|. It works by flushing
-\&{btex}$\,\ldots\,$\&{etex} and \&{verbatimtex}\allowbreak
-$\,\ldots\,$\&{etex} blocks, switching to the \.{MPX} file when it sees
-\&{btex}, and switching back when it sees \&{mpxbreak}.
-
-@d btex_code=0
-@d verbatim_code=1
-
-@ @<Put each...@>=
-primitive("btex",start_tex,btex_code);@/
-@!@:btex_}{\&{btex} primitive@>
-primitive("verbatimtex",start_tex,verbatim_code);
-@!@:verbatimtex_}{\&{verbatimtex} primitive@>
-primitive("etex",etex_marker,0); eqtb[frozen_etex]:=eqtb[cur_sym];@/
-@!@:etex_}{\&{etex} primitive@>
-primitive("mpxbreak",mpx_break,0); eqtb[frozen_mpx_break]:=eqtb[cur_sym];@/
-@!@:mpx_break_}{\&{mpxbreak} primitive@>
-
-@ @<Cases of |print_cmd...@>=
-start_tex: if m=btex_code then print("btex")
- else print("verbatimtex");
-etex_marker: print("etex");
-mpx_break: print("mpxbreak");
-
-@ Actually, |get_t_next| is a macro that avoids procedure overhead except
-in the unusual case where \&{btex}, \&{verbatimtex}, \&{etex}, or \&{mpxbreak}
-is encountered.
-
-@d get_t_next==begin get_next;
- if cur_cmd<=max_pre_command then t_next;
- end
-@d TeX_flush=65 {go here to flush to the next ``\&{etex}''}
-
-@p procedure@?start_mpx_input; forward;@t\2@>
-procedure t_next;
-label TeX_flush, common_ending;
-var @!old_status:normal..loop_defining; {saves the |scanner_status|}
-@!old_info:integer; {saves the |warning_info|}
-begin while cur_cmd<=max_pre_command do
- begin if cur_cmd=mpx_break then
- if not file_state or (mpx_name[index]=absent) then
- @<Complain about a misplaced \&{mpxbreak}@>
- else begin end_mpx_reading; goto TeX_flush;
- end
- else if cur_cmd=start_tex then
- if token_state or (name<=max_spec_src) then
- @<Complain that we are not reading a file@>
- else if mpx_reading then
- @<Complain that \.{MPX} files cannot contain \TeX\ material@>
- else if (cur_mod<>verbatim_code)and(mpx_name[index]<>finished) then
- begin if not begin_mpx_reading then start_mpx_input;
- end
- else goto TeX_flush
- else @<Complain about a misplaced \&{etex}@>;
- goto common_ending;
-TeX_flush: @<Flush the \TeX\ material@>;
-common_ending: get_next;
- end;
-end;
-
-@ We could be in the middle of an operation such as skipping false conditional
-text when \TeX\ material is encountered, so we must be careful to save the
-|scanner_status|.
-
-@<Flush the \TeX\ material@>=
-old_status:=scanner_status;
-old_info:=warning_info;
-scanner_status:=tex_flushing;
-warning_info:=line;
-repeat get_next;
-until cur_cmd=etex_marker;
-scanner_status:=old_status;
-warning_info:=old_info
-
-@ @<Complain that \.{MPX} files cannot contain \TeX\ material@>=
-begin print_err("An mpx file cannot contain btex or verbatimtex blocks");
-help4("This file contains picture expressions for btex...etex")@/
- ("blocks. Such files are normally generated automatically")@/
- ("but this one seems to be messed up. I'll just keep going")@/
- ("and hope for the best.");@/
-error;
-end
-
-@ @<Complain that we are not reading a file@>=
-begin print_err("You can only use `btex' or `verbatimtex' in a file");
-help3("I'll have to ignore this preprocessor command because it")@/
- ("only works when there is a file to preprocess. You might")@/
- ("want to delete everything up to the next `etex`.");@/
-error;
-end
-
-@ @<Complain about a misplaced \&{mpxbreak}@>=
-begin print_err("Misplaced mpxbreak");
-help2("I'll ignore this preprocessor command because it")@/
- ("doesn't belong here");@/
-error;
-end
-
-@ @<Complain about a misplaced \&{etex}@>=
-begin print_err("Extra etex will be ignored");
-help1("There is no btex or verbatimtex for this to match");@/
-error;
-end
-
-@* \[31] Scanning macro definitions.
-\MP\ 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_t_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_t_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 \MP's tables won't get fouled up.
-
-@p procedure get_symbol; {sets |cur_sym| to a safe symbol}
-label restart;
-begin restart: get_t_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_t_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_t_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_t_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_t_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_t_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);
-@:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
-incr(k); link(p):=r; r:=p; get_t_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_t_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_t_next;
- end;
-end
-
-@* \[32] Expanding the next token.
-Only a few command codes |<min_command| can possibly be returned by
-|get_t_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|.
-
-\MP\ usually gets the next token of input by saying |get_x_next|. This is
-like |get_t_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 34 below}
-end
-
-@ @<Expand the token after the next token@>=
-begin get_t_next;
-p:=cur_tok; get_t_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:=is_scantok;
-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);
-@:MetaPost 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_t_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_t_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 \MP's
-input stack, so that |get_t_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);
-tail:=null; l_delim:=null; r_delim:=null;
-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 print(text(n))
-else begin p:=info(a);
- if p=null then 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 `"); 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_t_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);
-@:MetaPost 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);
-@:MetaPost capacity exceeded parameter stack size}{\quad parameter stack size@>
- end;
-param_stack[param_ptr]:=p; incr(param_ptr);
-end;
-
-@* \[33] 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.
-
-\MP'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:=true_line;
-loop@+ begin get_t_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 \MP\ 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:=true_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, \MP\ 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:=true_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
-
-@* \[34] Iterations.
-To bring our treatment of |get_x_next| to a close, we need to consider what
-\MP\ 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)=progression_flag| means that
-|p=loop_list(loop_ptr)| points to a ``progression node'' and |value(p)|,
-|step_size(p)|, and |final_value(p)| contain the data for an arithmetic
-progression.
-
-\yskip\indent|loop_type(loop_ptr)=p>void| means that |p| points to an edge
-header and |loop_list(loop_ptr)| points into the graphical object list for
-that edge header.
-
-\yskip\noindent In the case of a progression node, the first word is not used
-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}
-@d progression_flag==null+2
- {|loop_type| value when |loop_list| points to a progression node}
-
-@<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 \MP\ 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 original author
-didn't write it until later. The reader may wish to come back to it.)
-
-@p procedure begin_iteration;
-label continue,done;
-var @!m:halfword; {|expr_base| (\&{for}) or |suffix_base| (\&{forsuffixes})}
-@!n:halfword; {hash address of the current symbol}
-@!s:pointer; {the new loop-control node}
-@!p:pointer; {substitution list for |scan_toks|}
-@!q:pointer; {link manipulation register}
-@!pp:pointer; {a new progression node}
-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;
- end
-else begin get_symbol; p:=get_node(token_node_size);
- info(p):=cur_sym; value(p):=m;@/
- get_x_next;
- if cur_cmd=within_token then @<Set up a picture iteration@>
- else begin @<Check for the |"="| or |":="| in a loop header@>;
- @<Scan the values to be used in the loop@>;
- end;
- end;
-@<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 |"="| or |":="| in a loop header@>=
-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
-
-@ @<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 \MP'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 \MP'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=progression_flag then
- begin p:=loop_list(loop_ptr); {now |p| points to a progression node}
- 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=null 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 if p=void then
- begin begin_token_list(info(loop_ptr),forever_text); return;
- end
-else @<Make |q| a capsule containing the next picture component from
- |loop_list(loop_ptr)| or |goto not_found|@>;
-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
-
-@ @<Make |q| a capsule containing the next picture component from...@>=
-begin q:=loop_list(loop_ptr);
-if q=null then goto not_found;
-skip_component(q)(goto not_found);
-cur_exp:=copy_objects(loop_list(loop_ptr),q);
-init_bbox(cur_exp);
-cur_type:=picture_type;@/
-loop_list(loop_ptr):=q;
-q:=stash_cur_exp;
-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=progression_flag then free_node(loop_list(loop_ptr),progression_node_size)
-else if p=null 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
-else if p>progression_flag then delete_edge_ref(p);
-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_list(s):=pp;
-loop_type(s):=progression_flag; goto done;
-end
-
-@ The last case is when we have just seen ``\&{within}'', and we need to
-parse a picture expression and prepare to iterate over it.
-
-@<Set up a picture iteration@>=
-begin get_x_next;
-scan_expression;
-@<Make sure the current expression is a known picture@>;
-loop_type(s):=cur_exp; cur_type:=vacuous;@/
-q:=link(dummy_loc(cur_exp));
-if q<> null then
- if is_start_or_stop(q) then
- if skip_1component(q)=null then q:=link(q);
-loop_list(s):=q;
-end
-
-@ @<Make sure the current expression is a known picture@>=
-if cur_type<>picture_type then
- begin
- disp_err(null,"Improper iteration spec has been replaced by nullpicture");
- help1("When you say `for x in p', p must be a known picture.");
- put_get_flush_error(get_node(edge_header_size));
- init_edges(cur_exp); cur_type:=picture_type;
- end
-
-@* \[35] 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@>
-
-\MP\ 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
-`\.{.mp}' unless otherwise specified; it is `\.{.log}' on the
-transcript file that records each run of \MP; it is `\.{.tfm}' on the font
-metric files that describe characters in any fonts created by \MP; it is
-`\.{.ps}' or `.{\it nnn}' for some number {\it nnn} on the \ps\ output files;
-and it is `\.{.mem}' on the mem files written by \.{INIMP} to initialize \MP.
-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, \MP\ will look for it on a special system
-area; this special area is intended for commonly used input files.
-
-Simple uses of \MP\ 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 \MP\ 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 \MP. 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.
-
-@ \MP\ uses the same conventions that have proved to be satisfactory for
-\TeX\ and \MF. In order to isolate the system-dependent aspects of file names,
-@^system dependencies@>
-the system-independent parts of \MP\ 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 \MP\ 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 \.{""}}
-
-@ It is easier to maintain reference counts if we assign initial values.
-
-@<Set init...@>=
-cur_name:=""; cur_area:=""; cur_ext:="";
-
-@ 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. Note that these variables
-cannot be of type |pool_pointer| because a string pool compaction could occur
-while scanning a file name.
-
-@<Glob...@>=
-@!area_delimiter:integer;
- {most recent `\.>' or `\.:' relative to |str_start[str_ptr]|}
-@!ext_delimiter:integer; {the relevant `\..', if any}
-
-@ Input files that can't be found in the user's area may appear in standard
-system areas called |MP_area| and |MF_area|. (The latter is used when the file
-extension is |".mf"|.) The standard system area for font metric files
-to be read is |MP_font_area|.
-This system area name will, of course, vary from place to place.
-@^system dependencies@>
-
-@d MP_area=="MPinputs:"
-@.MPinputs@>
-@d MF_area=="MFinputs:"
-@.MFinputs@>
-@d MP_font_area=="TeXfonts:"
-@.TeXfonts@>
-
-@ Here now is the first of the system-dependent routines for file name scanning.
-@^system dependencies@>
-
-@<Declare subroutines for parsing file names@>=
-procedure begin_name;
-begin delete_str_ref(cur_name); delete_str_ref(cur_area);
-delete_str_ref(cur_ext);@/
-area_delimiter:=-1; ext_delimiter:=-1;
-end;
-
-@ And here's the second.
-@^system dependencies@>
-
-@<Declare subroutines for parsing file names@>=
-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-str_start[str_ptr]; ext_delimiter:=-1;
- end
- else if (c=".")and(ext_delimiter<0) then
- ext_delimiter:=pool_ptr-str_start[str_ptr];
- str_room(1); append_char(c); {contribute |c| to the current string}
- more_name:=true;
- end;
-end;
-
-@ The third.
-@^system dependencies@>
-
-@<Declare subroutines for parsing file names@>=
-procedure end_name;
-var a,@!n,@!e:pool_pointer; {length of area, name, and extension}
-begin e:=pool_ptr-str_start[str_ptr]; {total length}
-if ext_delimiter<0 then ext_delimiter:=e;
-a:=area_delimiter+1; n:=ext_delimiter-a; e:=e-ext_delimiter;
-if a=0 then cur_area:=""
-else begin cur_area:=make_string;
- chop_last_string(str_start[cur_area]+a);
- end;
-if n=0 then cur_name:=""
-else begin cur_name:=make_string;
- chop_last_string(str_start[cur_name]+n);
- end;
-if e=0 then cur_ext:="" @+ else cur_ext:=make_string;
-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 print(a); print(n); print(e);
-end;
-
-@ Another system-dependent routine is needed to convert three internal
-\MP\ 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
-
-@<Declare subroutines for parsing file names@>=
-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_stop(a)-1 do append_to_name(so(str_pool[j]));
-for j:=str_start[n] to str_stop(n)-1 do append_to_name(so(str_pool[j]));
-for j:=str_start[e] to str_stop(e)-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 mem file names must be scanned
-before \MP's string mechanism has been initialized. We shall use the
-global variable |MP_mem_default| to supply the text for default system areas
-and extensions related to mem files.
-@^system dependencies@>
-
-@d mem_default_length=15 {length of the |MP_mem_default| string}
-@d mem_area_length=6 {length of its area part}
-@d mem_ext_length=4 {length of its `\.{.mem}' part}
-@d mem_extension=".mem" {the extension, as a \.{WEB} constant}
-
-@<Glob...@>=
-@!MP_mem_default:packed array[1..mem_default_length] of char;
-
-@ @<Set init...@>=
-MP_mem_default:='MPlib:plain.mem';
-@.MPlib@>
-@.plain@>
-@^system dependencies@>
-
-@ @<Check the ``constant'' values for consistency@>=
-if mem_default_length>file_name_size then bad:=20;
-
-@ Here is the messy routine that was just mentioned. It sets |name_of_file|
-from the first |n| characters of |MP_mem_default|, followed by
-|buffer[a..b]|, followed by the last |mem_ext_length| characters of
-|MP_mem_default|.
-
-We dare not give error messages here, since \MP\ 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 |MP_mem_default|}
-begin if n+b-a+1+mem_ext_length>file_name_size then
- b:=a+file_name_size-n-1-mem_ext_length;
-k:=0;
-for j:=1 to n do append_to_name(xord[MP_mem_default[j]]);
-for j:=a to b do append_to_name(buffer[j]);
-for j:=mem_default_length-mem_ext_length+1 to mem_default_length do
- append_to_name(xord[MP_mem_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'' \MP\ is trying to get going, just after
-the preliminary initialization, or when the user is substituting another
-mem 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_mem_file|@>=
-function open_mem_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(mem_file) then goto found;
- pack_buffered_name(mem_area_length,loc,j-1);
- {now try the system mem file area}
- if w_open_in(mem_file) then goto found;
- wake_up_terminal;
- wterm_ln('Sorry, I can''t find that mem file;',' 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(mem_default_length-mem_ext_length,1,0);
-if not w_open_in(mem_file) then
- begin wake_up_terminal;
- wterm_ln('I can''t find the PLAIN mem file!');
-@.I can't find PLAIN...@>
-@.plain@>
- open_mem_file:=false; return;
- end;
-found:loc:=j; open_mem_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 \MP\ 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 check for this before calling `|str_room|'.
-
-@p function make_name_string:str_number;
-var @!k:1..file_name_size; {index into |name_of_file|}
-begin if str_overflowed then
- make_name_string:="?"
-else begin str_room(name_length);
- 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 \MP\ 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 \MP.
-{\sl The {\logos METAFONT\/}book} 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;
-
-@ Here is another version that takes its input from a string.
-
-@<Declare subroutines for parsing file names@>=
-procedure str_scan_file(@!s:str_number);
-label done;
-var @!p,@!q:pool_pointer; {current position and stopping point}
-begin begin_name;
-p:=str_start[s]; q:=str_stop(s);
-while p<q do
- begin if not more_name(so(str_pool[p])) then goto done;
- incr(p);
- 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 `\.{ps}' and
-`\.{.mem}' and `\.{.tfm}' in order to make the names of \MP'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"|, |".mem"|, |".ps"|, or .\\{nnn}}
-begin add_str_ref(s);
-delete_str_ref(cur_name); delete_str_ref(cur_area);
-delete_str_ref(cur_ext);@/
-cur_area:=""; cur_ext:=s;
-cur_name:=job_name; pack_cur_name;
-end;
-
-@ If some trouble arises when \MP\ 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="" 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:="mpout";
-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 \MP\ 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);
-print(mem_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
-
-@ The |try_extension| function tries to open an input file determined by
-|cur_name|, |cur_area|, and the argument |ext|. It returns |false| if it
-can't find the file in |cur_area| or the appropriate system area.
-
-@p function try_extension(@!ext:str_number):boolean;
-begin pack_file_name(cur_name,cur_area,ext);
-in_name:=cur_name; in_area:=cur_area;
-if a_open_in(cur_file) then try_extension:=true
-else begin if str_vs_str(ext,".mf")=0 then in_area:=MF_area
- else in_area:=MP_area;
- pack_file_name(cur_name,in_area,ext);
- try_extension:=a_open_in(cur_file);
- end;
-end;
-
-@ After all calls to |try_extension|, we must make sure that we count references
-for |in_name| and |in_area| if they match |cur_name| and/or |cur_area|.
-
-@<Update the string reference counts for |in_name| and |in_area|@>=
-if in_name=cur_name then add_str_ref(cur_name);
-if in_area=cur_area then add_str_ref(cur_area)
-
-@ 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; {\MP\ will \.{input} something}
-label done;
-begin @<Put the desired file name in |(cur_name,cur_ext,cur_area)|@>;
-loop@+ begin begin_file_reading; {set up |cur_file| and new level of input}
- if cur_ext="" then
- if try_extension(".mp") then goto done
- else if try_extension("") then goto done
- else if try_extension(".mf") then goto done
- else do_nothing
- else if try_extension(cur_ext) then goto done;
- end_file_reading; {remove the level that didn't work}
- prompt_file_name("input file name","");
- end;
-done: name:=a_make_name_string(cur_file);
-@<Update the string reference counts for |in_name| and |in_area|@>;
-if job_name=0 then
- begin job_name:=cur_name; str_ref[job_name]:=max_str_ref;
- 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); print(name); update_terminal;
-@<Flush |name| and replace it with |cur_name| if it won't be needed@>;
-@<Read the first line of the new file@>;
-end;
-
-@ This code should be omitted if |a_make_name_string| returns something other
-than just a copy of its argument and the full file name is needed for opening
-\.{MPX} files or implementing the switch-to-editor option.
-@^system dependencies@>
-
-@<Flush |name| and replace it with |cur_name| if it won't be needed@>=
-flush_string(name); name:=cur_name; cur_name:=0
-
-@ 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
-
-@ Sometimes we need to deal with two file names at once. This procedure
-copies the given string into a special array for an old file name.
-
-@p procedure copy_old_name(s:str_number);
-var @!k:integer; {number of positions filled in |old_file_name|}
-@!j:pool_pointer; {index into |str_pool|}
-begin k:=0;
-for j:=str_start[s] to str_stop(s)-1 do
- begin incr(k);
- if k<=file_name_size then old_file_name[k]:=xchr[so(str_pool[j])];
- end;
-if k<=file_name_size then old_name_length:=k
-else old_name_length:=file_name_size;
-for k:=old_name_length+1 to file_name_size do @+old_file_name[k]:=' ';
-end;
-
-@ @<Glob...@>=
-@!old_file_name : packed array[1..file_name_size] of char;
- {analogous to |name_of_file|}
-@!old_name_length : 0..file_name_size;
- {this many relevant characters followed by blanks}
-
-@ The following simple routine starts reading the \.{MPX} file associated
-with the current input file.
-
-@p procedure start_mpx_input;
-label exit,not_found;
-var k:1..file_name_size;
-begin pack_file_name(in_name,in_area,".mpx");
-@<Try to make sure |name_of_file| refers to a valid \.{MPX} file and
- |goto not_found| if there is a problem@>;
-begin_file_reading;
-if not a_open_in(cur_file) then
- begin end_file_reading;
- goto not_found;
- end;
-name:=a_make_name_string(cur_file);
-mpx_name[index]:=name; add_str_ref(name);
-@<Read the first line of the new file@>;
-return;
-not_found: @<Explain that the \.{MPX} file can't be read and |succumb|@>;
-exit:end;
-
-@ This should ideally be changed to do whatever is necessary to create the
-\.{MPX} file given by |name_of_file| if it does not exist or if it is out
-of date. This requires invoking \.{MPtoTeX} on the |old_file_name| and passing
-the results through \TeX\ and \.{DVItoMP}. (It is possible to use a
-completely different typesetting program if suitable postprocessor is
-available to perform the function of \.{DVItoMP}.)
-@^system dependencies@>
-
-@<Try to make sure |name_of_file| refers to a valid \.{MPX} file and
- |goto not_found| if there is a problem@>=
-copy_old_name(name)
-{System-dependent code should be added here}
-
-@ @<Explain that the \.{MPX} file can't be read and |succumb|@>=
-if interaction=error_stop_mode then wake_up_terminal;
-print_nl(">> ");
-for k:=1 to old_name_length do print(xord[old_file_name[k]]);
-print_nl(">> ");
-for k:=1 to name_length do print(xord[name_of_file[k]]);
-print_nl("! Unable to make mpx file");
-help4("The two files given above are one of your source files")@/
- ("and an auxiliary file I need to read to find out what your")@/
- ("btex..etex blocks mean. If you don't know why I had trouble,")@/
- ("try running it manually through MPtoTeX, TeX, and DVItoMP");
-succumb;
-
-@ The last file-opening commands are for files accessed via the \&{readfrom}
-@:read_from_}{\&{readfrom} primitive@>
-operator and the \&{write} command. Such files are stored in separate arrays.
-@:write_}{\&{write} primitive@>
-
-@<Types in the outer block@>=
-readf_index = 0..max_read_files;
-write_index = 0..max_write_files;
-
-@ @<Glob...@>=
-rd_file:array [readf_index] of alpha_file; {\&{readfrom} files}
-rd_fname:array [readf_index] of str_number;
- {corresponding file name or 0 if file not open}
-read_files:readf_index; {number of valid entries in the above arrays}
-wr_file:array [write_index] of alpha_file; {\&{write} files}
-wr_fname:array [write_index] of str_number;
- {corresponding file name or 0 if file not open}
-write_files:write_index; {number of valid entries in the above arrays}
-
-@ @<Set init...@>=
-read_files:=0;
-write_files:=0;
-
-@ This routine starts reading the file named by string~|s| without setting
-|loc|, |limit|, or |name|. It returns |false| if the file is empty or cannot
-be opened. Otherwise it updates |rd_file[n]| and |rd_fname[n]|.
-
-@p function start_read_input(s:str_number; n:readf_index):boolean;
-label exit,not_found;
-begin str_scan_file(s);
-pack_cur_name;
-begin_file_reading;
-if not a_open_in(rd_file[n]) then goto not_found;
-if not input_ln(rd_file[n],false) then
- begin a_close(rd_file[n]); goto not_found; end;
-rd_fname[n]:=s;
-add_str_ref(s);
-start_read_input:=true;
-return;
-not_found: end_file_reading;
-start_read_input:=false;
-exit:end;
-
-@ Open |wr_file[n]| using file name~|s| and update |wr_fname[n]|.
-
-@p procedure open_write_file(s:str_number; n:readf_index);
-begin str_scan_file(s);
-pack_cur_name;
-while not a_open_out(wr_file[n]) do
- prompt_file_name("file name for write output","");
-wr_fname[n]:=s;
-add_str_ref(s);
-end;
-
-
-@* \[36] Introduction to the parsing routines.
-We come now to the central nervous system that sparks many of \MP's activities.
-By evaluating expressions, from their primary constituents to ever larger
-subexpressions, \MP\ builds the structures that ultimately define complete
-pictures or 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 node in a pen. Nobody
-else points to any of the nodes in this pen. The pen may be polygonal or
-elliptical.
-
-\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=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 edge header node.
-There may be other pointers to this particular set of edges. The header node
-contains a reference count that includes this particular reference.
-
-\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=color_type| means that |cur_exp| points to a |color_type|
-capsule node. The |value| part of this capsule
-points to a color node that contains three numeric values,
-each of which is |independent|, |dependent|, |proto_dependent|, or |known|.
-
-\smallskip\hang
-|cur_type=cmykcolor_type| means that |cur_exp| points to a |cmykcolor_type|
-capsule node. The |value| part of this capsule
-points to a color node that contains four 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, \MP\ 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 \MP'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,color_type,pair_type,dependent,proto_dependent,
- independent,cmykcolor_type: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 \MP\ 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,color_type,pair_type,dependent,proto_dependent,
- independent,cmykcolor_type: 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); v:=0;
-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(""""); print(v); print_char("""");
- end;
-pen_type,path_type,picture_type:@<Display a complex type@>;
-transform_type,color_type,pair_type,cmykcolor_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);
- path_type:print_path(v,"",false);
- picture_type:print_edges(v,"",false);
- 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,color_type,pair_type,@|
- dependent,proto_dependent,independent,cmykcolor_type:
- begin recycle_value(cur_exp); free_node(cur_exp,value_node_size);
- end;
-string_type:delete_str_ref(cur_exp);
-pen_type,path_type: toss_knot_list(cur_exp);
-picture_type:delete_edge_ref(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); v:=0;
-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);
-path_type,pen_type:toss_knot_list(v);
-picture_type:delete_edge_ref(v);
-cmykcolor_type,pair_type,color_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 |var_flag| is set to a special command code
-just before \MP\ 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;
-
-@* \[37] 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 signaled.
-
-@<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 |my_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 rest of a delimited set 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
-
-@ This code uses the fact that |red_part_loc| and |green_part_loc|
-are synonymous with |x_part_loc| and |y_part_loc|.
-
-@<Scan the rest of a delimited set of numerics@>=
-begin p:=stash_cur_exp;
-get_x_next; scan_expression;
-@<Make sure the second part of a pair or color has a numeric type@>;
-q:=get_node(value_node_size); name_type(q):=capsule;
-if cur_cmd=comma then type(q):=color_type
-else type(q):=pair_type;
-init_big_node(q); r:=value(q);
-stash_in(y_part_loc(r));
-unstash_cur_exp(p);
-stash_in(x_part_loc(r));
-if cur_cmd=comma then @<Scan the last of a triplet of numerics@>;
-if cur_cmd=comma then begin
- type(q):=cmykcolor_type;
- init_big_node(q); t:=value(q);
- mem[cyan_part_loc(t)]:=mem[red_part_loc(r)];
- value(cyan_part_loc(t)):=value(red_part_loc(r));
- mem[magenta_part_loc(t)]:=mem[green_part_loc(r)];
- value(magenta_part_loc(t)):=value(green_part_loc(r));
- mem[yellow_part_loc(t)]:=mem[blue_part_loc(r)];
- value(yellow_part_loc(t)):=value(blue_part_loc(r));
- recycle_value(r);
- r:=t;
- @<Scan the last of a quartet of numerics@>;
- end;
-check_delimiter(l_delim,r_delim);
-cur_type:=type(q);
-cur_exp:=q;
-end
-
-@ @<Make sure the second part of a pair or color has a numeric type@>=
-if cur_type<known then
- begin exp_err("Nonnumeric ypart has been replaced by 0");
-@.Nonnumeric...replaced by 0@>
- help4("I've started to scan a pair `(a,b)' or a color `(a,b,c)';")@/
- ("but after finding a nice `a' I found a `b' that isn't")@/
- ("of numeric type. So I've changed that part to zero.")@/
- ("(The b that I didn't like appears above the error message.)");
- put_get_flush_error(0);
- end
-
-@ @<Scan the last of a triplet of numerics@>=
-begin get_x_next; scan_expression;
-if cur_type<known then
- begin exp_err("Nonnumeric third part has been replaced by 0");
-@.Nonnumeric...replaced by 0@>
- help3("I've just scanned a color `(a,b,c)' or cmykcolor(a,b,c,d); but the `c' isn't")@/
- ("of numeric type. So I've changed that part to zero.")@/
- ("(The c that I didn't like appears above the error message.)");@/
- put_get_flush_error(0);
- end;
-stash_in(blue_part_loc(r));
-end
-
-@ @<Scan the last of a quartet of numerics@>=
-begin get_x_next; scan_expression;
-if cur_type<known then
- begin exp_err("Nonnumeric blackpart has been replaced by 0");
-@.Nonnumeric...replaced by 0@>
- help3("I've just scanned a cmykcolor `(c,m,y,k)'; but the `k' isn't")@/
- ("of numeric type. So I've changed that part to zero.")@/
- ("(The k that I didn't like appears above the error message.)");@/
- put_get_flush_error(0);
- end;
-stash_in(black_part_loc(r));
-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:=true_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<color_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;
-macro_ref:=null;
-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;
-picture_type:begin cur_exp:=value(p);add_edge_ref(cur_exp);
- end;
-pen_type:cur_exp:=copy_pen(value(p));
-path_type:cur_exp:=copy_path(value(p));
-transform_type,color_type,cmykcolor_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}, \&{color}, 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
-
-@* \[38] 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;
-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;
-
-@ 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; t:=endpoint; x:=0; y:=0; mac_name:=null;
-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 \MP\ 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; originator(q):=metapost_user; 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;
-
-@* \[39] 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 \MP, 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("readfrom",unary,read_from_op);@/
-@!@:read_from_}{\&{readfrom} primitive@>
-primitive("closefrom",unary,close_from_op);@/
-@!@:close_from_}{\&{closefrom} 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("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("redpart",unary,red_part);@/
-@!@:red_part_}{\&{redpart} primitive@>
-primitive("greenpart",unary,green_part);@/
-@!@:green_part_}{\&{greenpart} primitive@>
-primitive("bluepart",unary,blue_part);@/
-@!@:blue_part_}{\&{bluepart} primitive@>
-primitive("cyanpart",unary,cyan_part);@/
-@!@:cyan_part_}{\&{cyanpart} primitive@>
-primitive("magentapart",unary,magenta_part);@/
-@!@:magenta_part_}{\&{magentapart} primitive@>
-primitive("yellowpart",unary,yellow_part);@/
-@!@:yellow_part_}{\&{yellowpart} primitive@>
-primitive("blackpart",unary,black_part);@/
-@!@:black_part_}{\&{blackpart} primitive@>
-primitive("greypart",unary,grey_part);@/
-@!@:grey_part_}{\&{greypart} primitive@>
-primitive("colormodel",unary,color_model_part);@/
-@!@:color_model_part_}{\&{colormodel} primitive@>
-primitive("fontpart",unary,font_part);@/
-@!@:font_part_}{\&{fontpart} primitive@>
-primitive("textpart",unary,text_part);@/
-@!@:text_part_}{\&{textpart} primitive@>
-primitive("pathpart",unary,path_part);@/
-@!@:path_part_}{\&{pathpart} primitive@>
-primitive("penpart",unary,pen_part);@/
-@!@:pen_part_}{\&{penpart} primitive@>
-primitive("dashpart",unary,dash_part);@/
-@!@:dash_part_}{\&{dashpart} 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("fontsize",unary,font_size);@/
-@!@:font_size_}{\&{fontsize} primitive@>
-primitive("llcorner",unary,ll_corner_op);@/
-@!@:ll_corner_}{\&{llcorner} primitive@>
-primitive("lrcorner",unary,lr_corner_op);@/
-@!@:lr_corner_}{\&{lrcorner} primitive@>
-primitive("ulcorner",unary,ul_corner_op);@/
-@!@:ul_corner_}{\&{ulcorner} primitive@>
-primitive("urcorner",unary,ur_corner_op);@/
-@!@:ur_corner_}{\&{urcorner} primitive@>
-primitive("arclength",unary,arc_length);@/
-@!@:arc_length_}{\&{arclength} primitive@>
-primitive("angle",unary,angle_op);@/
-@!@:angle_}{\&{angle} primitive@>
-primitive("cycle",cycle,cycle_op);@/
-@!@:cycle_}{\&{cycle} primitive@>
-primitive("stroked",unary,stroked_op);@/
-@!@:stroked_}{\&{stroked} primitive@>
-primitive("filled",unary,filled_op);@/
-@!@:filled_}{\&{filled} primitive@>
-primitive("textual",unary,textual_op);@/
-@!@:textual_}{\&{textual} primitive@>
-primitive("clipped",unary,clipped_op);@/
-@!@:clipped_}{\&{clipped} primitive@>
-primitive("bounded",unary,bounded_op);@/
-@!@:bounded_}{\&{bounded} 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("or",tertiary_binary,or_op);@/
-@!@:or_}{\&{or} primitive@>
-primitive("and",and_command,and_op);@/
-@!@:and_}{\&{and} 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("arctime",primary_binary,arc_time_of);@/
-@!@:arc_time_of_}{\&{arctime} primitive@>
-primitive("mpversion",nullary,mp_version);@/
-@!@:mp_verison_}{\&{mpversion} 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("infont",secondary_binary,in_font);@/
-@!@:in_font_}{\&{infont} 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 @t\4@>@<Declare nullary action procedure@>@;
-procedure do_nullary(@!c:quarterword);
-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:=get_pen_circle(0);
- end;
-normal_deviate:begin cur_type:=known; cur_exp:=norm_rand;
- end;
-pen_circle:begin cur_type:=pen_type; cur_exp:=get_pen_circle(unity);
- end;
-job_name_op: begin if job_name=0 then open_log_file;
- cur_type:=string_type; cur_exp:=job_name;
- end;
-mp_version: begin cur_type:=string_type; cur_exp:=metapost_version; end;
-read_string_op:@<Read a string from the terminal@>;
-end; {there are no other cases}
-check_arith;
-end;
-
-@ @<Read a string...@>=
-begin if interaction<=nonstop_mode then
- fatal_error("*** (cannot readstring in nonstop modes)");
-begin_file_reading; name:=is_read;
-limit:=start; prompt_input("");
-finish_read;
-end
-
-@ @<Declare nullary action procedure@>=
-procedure finish_read; {copy |buffer| line to |cur_exp|}
-var @!k:pool_pointer;
-begin 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,@!r: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<color_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;
-
-@ The |nice_color_or_pair| function is analogous except that it also accepts
-fully known colors.
-
-@<Declare unary action procedures@>=
-function nice_color_or_pair(@!p:integer;@!t:quarterword):boolean;
-label exit;
-var @!q,@!r:pointer; {for scanning the big node}
-begin if (t<>pair_type)and(t<>color_type)and(t<>cmykcolor_type) then
- nice_color_or_pair:=false
-else begin q:=value(p);
- r:=q+big_node_size[type(p)];
- repeat r:=r-2;
- if type(r)<>known then
- begin nice_color_or_pair:=false; return;
- end;
- until r=q;
- nice_color_or_pair:=true;
- end;
-exit:end;
-
-@ @<Declare unary action...@>=
-procedure print_known_or_unknown_type(@!t:small_number;@!v:integer);
-begin print_char("(");
-if t>known then print("unknown numeric")
-else begin if (t=pair_type)or(t=color_type)or(t=cmykcolor_type) then
- if not nice_color_or_pair(v,t) then print("unknown ");
- print_type(t);
- end;
-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
-color_type,cmykcolor_type,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 {|color_type| or |pair_type|}
- begin p:=value(cur_exp);
- r:=p+big_node_size[cur_type];
- repeat r:=r-2;
- if type(r)=known then negate(value(r))
- else negate_dep_list(dep_list(r));
- until r=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);
-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;
-
-@
-@d pict_color_type(#)==(link(dummy_loc(cur_exp))<>null) and
- (has_color(link(dummy_loc(cur_exp)))) and
- ((color_model(link(dummy_loc(cur_exp)))=#)
- or
- (color_model(link(dummy_loc(cur_exp)))=uninitialized_model) and
- ((internal[default_color_model] div unity)=#))
-
-@<Additional cases of unary operators@>=
-x_part,y_part:if (cur_type=pair_type)or(cur_type=transform_type) then
- take_part(c)
- else if cur_type=picture_type then take_pict_part(c)
- else bad_unary(c);
-xx_part,xy_part,yx_part,yy_part: if cur_type=transform_type then take_part(c)
- else if cur_type=picture_type then take_pict_part(c)
- else bad_unary(c);
-red_part,green_part,blue_part:
- if cur_type=color_type then take_part(c)
- else if cur_type=picture_type then begin
- if pict_color_type(rgb_model) then take_pict_part(c) else bad_color_part(c);
- end else bad_unary(c);
-cyan_part,magenta_part,yellow_part,black_part:
- if cur_type=cmykcolor_type then take_part(c)
- else if cur_type=picture_type then begin
- if pict_color_type(cmyk_model) then take_pict_part(c) else bad_color_part(c);
- end else bad_unary(c);
-grey_part: if cur_type=known then cur_exp:=value(c)
- else if cur_type=picture_type then begin
- if pict_color_type(grey_model) then take_pict_part(c) else bad_color_part(c);
- end else bad_unary(c);
-color_model_part: if cur_type=picture_type then take_pict_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+sector_offset[c+x_part_sector-x_part]);
-recycle_value(temp_val);
-end;
-
-@ The manual states that asking for the \&{redpart} of a non-colored object
-is legal, so no error is given in that case.
-
-@<Declare unary action...@>=
-procedure bad_color_part(@!c:quarterword);
-var @!p:pointer; {the big node}
-begin
- p:=link(dummy_loc(cur_exp));
- if has_color(p) then begin
- exp_err("Wrong picture color model: "); print_op(c);
-@.Wrong picture color model...@>
- if color_model(p)=grey_model then
- print(" of grey object")
- else if color_model(p)=cmyk_model then
- print(" of cmyk object")
- else if color_model(p)=rgb_model then
- print(" of rgb object")
- else if color_model(p)=no_model then
- print(" of marking object")
- else
- print(" of defaulted object");
- help3("You can only ask for the redpart, greenpart, bluepart of a rgb object,")@/
- ("the cyanpart, magentapart, yellowpart or blackpart of a cmyk object, ")@/
- ("or the greypart of a grey object. No mixing and matching, please.");
- error;
- end;
- if c=black_part then
- flush_cur_exp(unity)
- else
- flush_cur_exp(0);
-end;
-
-@ @<Initialize table entries...@>=
-name_type(temp_val):=capsule;
-
-@ @<Additional cases of unary operators@>=
-font_part,text_part,path_part,pen_part,dash_part:
- if cur_type=picture_type then take_pict_part(c)
- else bad_unary(c);
-
-@ @<Declare unary action...@>=
-procedure@?scale_edges; forward;@t\2@>@;@/
-procedure take_pict_part(@!c:quarterword);
-label exit, not_found;
-var @!p:pointer; {first graphical object in |cur_exp|}
-begin p:=link(dummy_loc(cur_exp));
-if p<>null then
- begin case c of
- x_part,y_part,xx_part,xy_part,yx_part,yy_part:
- if type(p)=text_code then flush_cur_exp(text_trans_part(p+c))
- else goto not_found;
- red_part,green_part,blue_part:
- if has_color(p) then flush_cur_exp(obj_color_part(p+c))
- else goto not_found;
- cyan_part,magenta_part,yellow_part,black_part:
- if has_color(p) then
- if (color_model(p)=uninitialized_model)and(c=black_part) then
- flush_cur_exp(unity)
- else
- flush_cur_exp(obj_color_part(p+c+(red_part-cyan_part)))
- else goto not_found;
- grey_part:
- if has_color(p) then
- flush_cur_exp(obj_color_part(p+c+(red_part-grey_part)))
- else goto not_found;
- color_model_part:
- if has_color(p) then
- if color_model(p)=uninitialized_model then
- flush_cur_exp(internal[default_color_model])
- else
- flush_cur_exp(color_model(p)*unity)
- else goto not_found;
- @<Handle other cases in |take_pict_part| or |goto not_found|@>@;
- end; {all cases have been enumerated}
- return;
- end;
-not_found:@<Convert the current expression to a null value appropriate
- for |c|@>;
-exit:end;
-
-@ @<Handle other cases in |take_pict_part| or |goto not_found|@>=
-text_part: if type(p)<>text_code then goto not_found
- else begin flush_cur_exp(text_p(p));
- add_str_ref(cur_exp);
- cur_type:=string_type;
- end;
-font_part: if type(p)<>text_code then goto not_found
- else begin flush_cur_exp(font_name[font_n(p)]);
- add_str_ref(cur_exp);
- cur_type:=string_type;
- end;
-path_part:if type(p)=text_code then goto not_found
- else if is_stop(p) then confusion("pict")
-@:this can't happen pict}{\quad pict@>
- else begin flush_cur_exp(copy_path(path_p(p)));
- cur_type:=path_type;
- end;
-pen_part: if not has_pen(p) then goto not_found
- else if pen_p(p)=null then goto not_found
- else begin flush_cur_exp(copy_pen(pen_p(p)));
- cur_type:=pen_type;
- end;
-dash_part: if type(p)<>stroked_code then goto not_found
- else if dash_p(p)=null then goto not_found
- else begin add_edge_ref(dash_p(p));@/
- se_sf:=dash_scale(p);
- se_pic:=dash_p(p);
- scale_edges;
- flush_cur_exp(se_pic);
- cur_type:=picture_type;
- end;
-
-@ Since |scale_edges| had to be declared |forward|, it had to be declared as a
-parameterless procedure even though it really takes two arguments and updates
-one of them. Hence the following globals are needed.
-
-@<Global...@>=
-@!se_pic:pointer; {edge header used and updated by |scale_edges|}
-@!se_sf:scaled; {the scale factor argument to |scale_edges|}
-
-@ @<Convert the current expression to a null value appropriate...@>=
-case c of
-text_part,font_part: begin flush_cur_exp("");
- cur_type:=string_type;
- end;
-path_part: begin flush_cur_exp(get_node(knot_node_size));
- left_type(cur_exp):=endpoint;
- right_type(cur_exp):=endpoint;
- link(cur_exp):=cur_exp;
- x_coord(cur_exp):=0;
- y_coord(cur_exp):=0;
- originator(cur_exp):=metapost_user;
- cur_type:=path_type;
- end;
-pen_part: begin flush_cur_exp(get_pen_circle(0));
- cur_type:=pen_type;
- end;
-dash_part: begin flush_cur_exp(get_node(edge_header_size));
- init_edges(cur_exp);
- cur_type:=picture_type;
- end;
-othercases flush_cur_exp(0)
-endcases
-
-@ @<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;
- 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);
-font_size: if cur_type<>string_type then bad_unary(font_size)
- else @<Find the design size of the font whose name is |cur_exp|@>;
-
-@ @<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_stop(cur_exp)-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
- if internal[warning_check]>0 then
- begin print_err("Number too large ("); print_int(n); print_char(")");
-@.Number too large@>
- help2("I have trouble with numbers greater than 4095; watch out.")@/
- ("(Set warningcheck:=0 to suppress this message.)");
- 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: case cur_type of
- string_type: flush_cur_exp(length(cur_exp)*unity);
- path_type: flush_cur_exp(path_length);
- known: cur_exp:=abs(cur_exp);
- picture_type: flush_cur_exp(pict_length);
- othercases 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)
- endcases;
-
-@ @<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;
-
-@ @<Declare unary action...@>=
-function pict_length:scaled; {counts interior components in picture |cur_exp|}
-label found;
-var @!n:scaled; {the count so far}
-@!p:pointer; {traverser}
-begin n:=0;
-p:=link(dummy_loc(cur_exp));
-if p<>null then
- begin if is_start_or_stop(p) then
- if skip_1component(p)=null then p:=link(p);
- while p<>null do
- begin skip_component(p)(goto found);
- n:=n+unity;
- end;
- end;
-found:pict_length:=n;
-end;
-
-@ Implement |turningnumber|
-
-@<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
- flush_cur_exp(turn_cycles_wrapper(cur_exp));
- end;
-
-@ The function |an_angle| returns the value of the |angle| primitive, or $0$ if the
-argument is |origin|.
-
-@<Declare unary action...@>=
-function an_angle (@!xpar,@!ypar:scaled):angle;
-begin
- if (not ((xpar=0) and (ypar=0))) then
- an_angle := n_arg(xpar,ypar)
- else
- an_angle := 0;
-end;
-
-@ The actual turning number is (for the moment) computed in a C function
-that receives eight integers corresponding to the four controlling points,
-and returns a single angle. Besides those, we have to account for discrete
-moves at the actual points.
-
-@d p_nextnext==link(link(p))
-@d p_next==link(p)
-
-@d seven_twenty_deg==@'5500000000 {$720\cdot2^{20}$, represents $720^\circ$}
-
-@<Declare unary action...@>=
-function new_turn_cycles (@!c:pointer):scaled;
-label exit;
-var @!res,ang:angle; { the angles of intermediate results }
-@!turns:scaled; { the turn counter }
-@!p:pointer; { for running around the path }
-@!xp,yp:integer; { coordinates of next point }
-@!x,y:integer; { helper coordinates }
-@!in_angle,out_angle:angle; { helper angles}
-@!old_setting:0..max_selector; {saved |selector| setting}
-begin
-res:=0;
-turns:= 0;
-p:=c;
-old_setting := selector; selector:=term_only;
-if internal[tracing_commands]>unity then begin
- begin_diagnostic;
- print_nl("");
- end_diagnostic(false);
-end;
-repeat
- xp := x_coord(p_next); yp := y_coord(p_next);
- ang := bezier_slope(x_coord(p), y_coord(p), right_x(p), right_y(p),
- left_x(p_next), left_y(p_next), xp, yp, internal[tracing_commands]);
- if ang>seven_twenty_deg then begin
- print_err("Strange path");
- error;
- new_turn_cycles := 0;
- return;
- end;
- res := res + ang;
- if res > one_eighty_deg then begin
- res := res - three_sixty_deg;
- turns := turns + unity;
- end;
- if res <= -one_eighty_deg then begin
- res := res + three_sixty_deg;
- turns := turns - unity;
- end;
- { incoming angle at next point }
- x := left_x(p_next); y := left_y(p_next);
- if (xp=x)and(yp=y) then begin x := right_x(p); y := right_y(p); end;
- if (xp=x)and(yp=y) then begin x := x_coord(p); y := y_coord(p); end;
- in_angle := an_angle(xp - x, yp - y);
- { outgoing angle at next point }
- x := right_x(p_next); y := right_y(p_next);
- if (xp=x)and(yp=y) then begin x := left_x(p_nextnext); y := left_y(p_nextnext); end;
- if (xp=x)and(yp=y) then begin x := x_coord(p_nextnext); y := y_coord(p_nextnext); end;
- out_angle := an_angle(x - xp, y- yp);
- ang := (out_angle - in_angle);
- reduce_angle(ang);
- if ang<>0 then begin
- res := res + ang;
- if res >= one_eighty_deg then begin
- res := res - three_sixty_deg;
- turns := turns + unity;
- end;
- if res <= -one_eighty_deg then begin
- res := res + three_sixty_deg;
- turns := turns - unity;
- end;
- end;
- p := link(p);
-until p=c;
-new_turn_cycles := turns;
-exit:
-selector:=old_setting;
-end;
-
-
-@ This code is based on Bogus\l{}av Jackowski's
-|emergency_turningnumber| macro, with some minor changes by Taco
-Hoekwater. The macro code looked more like this:
-{\obeylines
-vardef turning\_number primary p =
-~~save res, ang, turns;
-~~res := 0;
-~~if length p <= 2:
-~~~~if Angle ((point 0 of p) - (postcontrol 0 of p)) >= 0: 1 else: -1 fi
-~~else:
-~~~~for t = 0 upto length p-1 :
-~~~~~~angc := Angle ((point t+1 of p) - (point t of p))
-~~~~~~~~- Angle ((point t of p) - (point t-1 of p));
-~~~~~~if angc > 180: angc := angc - 360; fi;
-~~~~~~if angc < -180: angc := angc + 360; fi;
-~~~~~~res := res + angc;
-~~~~endfor;
-~~res/360
-~~fi
-enddef;}
-The general idea is to calculate only the sum of the angles of straight lines between
-the points, of a path, not worrying about cusps or self-intersections in the segments
-at all. If the segment is not well-behaved, the result is not necesarily correct. But
-the old code was not always correct either, and worse, it sometimes failed for well-behaved
-paths as well. All known bugs that were triggered by the original code no longer occur
-with this code, and it runs roughly 3 times as fast because the algorithm is much simpler.
-
-@ It is possible to overflow the return value of the |turn_cycles|
-function when the path is sufficiently long and winding, but I am not
-going to bother testing for that. In any case, it would only return
-the looped result value, which is not a big problem.
-
-The macro code for the repeat loop was a bit nicer to look
-at than the pascal code, because it could use |point -1 of p|. In
-pascal, the fastest way to loop around the path is not to look
-backward once, but forward twice. These defines help hide the trick.
-
-@d p_to==link(link(p))
-@d p_here==link(p)
-@d p_from==p
-
-@<Declare unary action...@>=
-function turn_cycles (@!c:pointer):scaled;
-var @!res,ang:angle; { the angles of intermediate results }
-@!turns:scaled; { the turn counter }
-@!p:pointer; { for running around the path }
-begin res:=0; turns:= 0; p:=c;
-repeat
- ang := an_angle (x_coord(p_to) - x_coord(p_here), y_coord(p_to) - y_coord(p_here))
- - an_angle (x_coord(p_here) - x_coord(p_from), y_coord(p_here) - y_coord(p_from));
- reduce_angle(ang);
- res := res + ang;
- if res >= three_sixty_deg then begin
- res := res - three_sixty_deg;
- turns := turns + unity;
- end;
- if res <= -three_sixty_deg then begin
- res := res + three_sixty_deg;
- turns := turns - unity;
- end;
- p := link(p);
-until p=c;
-turn_cycles := turns;
-end;
-
-@ @<Declare unary action...@>=
-function turn_cycles_wrapper (@!c:pointer):scaled;
- var nval,oval:scaled;
- saved_t_o:scaled; {tracing\_online saved }
-begin
- if (link(c)=c)or(link(link(c))=c) then
- if an_angle (x_coord(c) - right_x(c), y_coord(c) - right_y(c)) > 0 then
- turn_cycles_wrapper := unity
- else
- turn_cycles_wrapper := -unity
- else begin
- nval := new_turn_cycles(c);
- oval := turn_cycles(c);
- if nval<>oval then begin
- saved_t_o:=internal[tracing_online];
- internal[tracing_online]:=unity;
- begin_diagnostic;
- print_nl ("Warning: the turningnumber algorithms do not agree. The current computed value is ");
- print_scaled(nval);
- print(", but the 'connect-the-dots' algorithm returned ");
- print_scaled(oval);
- end_diagnostic(false);
- internal[tracing_online]:=saved_t_o;
- end;
- turn_cycles_wrapper := nval;
- end;
-end;
-
-@ @<Declare unary action...@>=
-function count_turns(@!c:pointer):scaled;
-var @!p:pointer; {a knot in envelope spec |c|}
-@!t:integer; {total pen offset changes counted}
-begin t:=0; p:=c;
-repeat t:=t+info(p)-zero_off;
-p:=link(p);
-until p=c;
-count_turns:=(t div 3)*unity;
-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)(unknown_pen);
-path_type: type_range(path_type)(unknown_path);
-picture_type: type_range(picture_type)(unknown_picture);
-transform_type,color_type,cmykcolor_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,path_type,picture_type,
- known: b:=true_code;
-transform_type,color_type,cmykcolor_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@>=
-arc_length: begin if cur_type=pair_type then pair_to_path;
- if cur_type<>path_type then bad_unary(arc_length)
- else flush_cur_exp(get_arc_length(cur_exp));
- end;
-
-@ Here we use the fact that |c-filled_op+fill_code| is the desired graphical
-object |type|.
-@^data structure assumptions@>
-
-@<Additional cases of unary operators@>=
-filled_op,stroked_op,textual_op,clipped_op,bounded_op:
- begin if cur_type<>picture_type then flush_cur_exp(false_code)
- else if link(dummy_loc(cur_exp))=null then flush_cur_exp(false_code)
- else if type(link(dummy_loc(cur_exp)))=c+fill_code-filled_op 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 bad_unary(make_pen_op)
- else begin cur_type:=pen_type;
- cur_exp:=make_pen(cur_exp,true);
- end;
- end;
-make_path_op: if cur_type<>pen_type then bad_unary(make_path_op)
- else begin cur_type:=path_type;
- make_path(cur_exp);
- end;
-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);
-
-@ The |pair_value| routine changes the current expression to a
-given ordered pair of values.
-
-@<Declare unary action procedures@>=
-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 unary operators@>=
-ll_corner_op: if not get_cur_bbox then bad_unary(ll_corner_op)
- else pair_value(minx,miny);
-lr_corner_op: if not get_cur_bbox then bad_unary(lr_corner_op)
- else pair_value(maxx,miny);
-ul_corner_op: if not get_cur_bbox then bad_unary(ul_corner_op)
- else pair_value(minx,maxy);
-ur_corner_op: if not get_cur_bbox then bad_unary(ur_corner_op)
- else pair_value(maxx,maxy);
-
-@ Here is a function that sets |minx|, |maxx|, |miny|, |maxy| to the bounding
-box of the current expression. The boolean result is |false| if the expression
-has the wrong type.
-
-@<Declare unary action procedures@>=
-function get_cur_bbox: boolean;
-label exit;
-begin case cur_type of
-picture_type: begin set_bbox(cur_exp,true);
- if minx_val(cur_exp)>maxx_val(cur_exp) then
- begin minx:=0; maxx:=0; miny:=0; maxy:=0;
- end
- else begin minx:=minx_val(cur_exp);
- maxx:=maxx_val(cur_exp);
- miny:=miny_val(cur_exp);
- maxy:=maxy_val(cur_exp);
- end;
- end;
-path_type: path_bbox(cur_exp);
-pen_type: pen_bbox(cur_exp);
-othercases begin get_cur_bbox:=false;
- return;
- end
-endcases;@/
-get_cur_bbox:=true;
-exit:end;
-
-@ @<Additional cases of unary operators@>=
-read_from_op,close_from_op: if cur_type<>string_type then bad_unary(c)
-else do_read_or_close(c);
-
-@ Here is a routine that interprets |cur_exp| as a file name and tries to read
-a line from the file or to close the file.
-
-@d close_file=46 {go here when closing the file}
-
-@<Declare unary action procedures@>=
-procedure do_read_or_close(@!c:quarterword);
-label exit, continue, found, not_found, close_file;
-var @!n,@!n0:readf_index; {indices for searching |rd_fname|}
-begin @<Find the |n| where |rd_fname[n]=cur_exp|; if |cur_exp| must be inserted,
- call |start_read_input| and |goto found| or |not_found|@>;
-begin_file_reading;
-name:=is_read;
-if input_ln(rd_file[n],true) then goto found;
-end_file_reading;
-not_found:@<Record the end of file and set |cur_exp| to a dummy value@>;
-return;
-close_file:flush_cur_exp(0); cur_type:=vacuous; return;
-found:flush_cur_exp(0);
-finish_read;
-exit:end;
-
-@ Free slots in the |rd_file| and |rd_fname| arrays are marked with 0's in
-|rd_fname|.
-
-@<Find the |n| where |rd_fname[n]=cur_exp|...@>=
-n:=read_files;
-n0:=read_files;
-repeat
-continue:if n>0 then decr(n)
-else if c=close_from_op then goto close_file
-else @<Insert |cur_exp| at index |n0|, then call |start_read_input| and
- |goto found| or |not_found|@>;
-if rd_fname[n]=0 then
- begin n0:=n; goto continue;
- end;
-until str_vs_str(cur_exp,rd_fname[n])=0;
-if c=close_from_op then
- begin a_close(rd_file[n]); goto not_found; end
-
-@ @<Insert |cur_exp| at index |n0|, then call |start_read_input| and...@>=
-begin if n0=read_files then
- if read_files<max_read_files then incr(read_files)
- else overflow("readfrom files",max_read_files);
-n:=n0;
-if start_read_input(cur_exp,n) then goto found @+else goto not_found;
-end
-
-@ @<Record the end of file and set |cur_exp| to a dummy value@>=
-delete_str_ref(rd_fname[n]);
-rd_fname[n]:=0;
-if n=read_files-1 then read_files:=n;
-if c=close_from_op then goto close_file;
-@<Make sure |eof_line| is initialized@>;
-flush_cur_exp(eof_line);
-cur_type:=string_type
-
-@ Since the |eof_line| string contains a non-printable character, it must be
-initialized at run time and stored in a global variable.
-
-@<Glob...@>=
-eof_line:str_number; {string denoting end-of-file or 0 if uninitialized}
-
-@ @<Set init...@>=
-eof_line:=0;
-
-@ @<Make sure |eof_line| is initialized@>=
-if eof_line=0 then
- begin append_char(0);
- eof_line:=make_string;
- str_ref[eof_line]:=max_str_ref;
- end
-
-@ 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,color_type,cmykcolor_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,color_type,cmykcolor_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<color_type)or(type(p)<color_type) then bad_binary(p,c)
-else if (cur_type>pair_type)and(type(p)>pair_type) then
- add_or_subtract(p,null,c)
- else if cur_type<>type(p) then bad_binary(p,c)
- else begin q:=value(p); r:=value(cur_exp);
- rr:=r+big_node_size[cur_type];
- while r<rr do
- begin add_or_subtract(q,r,c);
- q:=q+2; r:=r+2;
- end;
- end
-
-@ 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 check_arith; {at this point |arith_error| should be |false|?}
- 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)and(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: arith_error:=false; {ignore overflow in comparisons}
- 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(name_type(r)+x_part-x_part_sector);
-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<color_type)or(type(p)<color_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_color_or_pair(p,type(p))and(cur_type>pair_type))
- or(nice_color_or_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)or(cur_type=color_type)or
- (cur_type=cmykcolor_type) then
- begin p:=value(cur_exp)+big_node_size[cur_type];
- repeat p:=p-2;
- dep_mult(p,v,true);
- until p=value(cur_exp);
- 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>=color_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,color_type,cmykcolor_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)+big_node_size[cur_type];
- repeat p:=p-2;
- dep_mult(p,v,false);
- until p=value(cur_exp);
- 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 color or pair by a dependency list.
-
-@<Declare binary action procedures@>=
-procedure hard_times(@!p:pointer);
-label done;
-var @!q:pointer; {a copy of the dependent variable |p|}
-@!r:pointer; {a component of the big node for the nice color or pair}
-@!v:scaled; {the known value for |r|}
-begin if type(p)<=pair_type then
- begin q:=stash_cur_exp; unstash_cur_exp(p); p:=q;
- end; {now |cur_type=pair_type| or |cur_type=color_type|}
-r:=value(cur_exp)+big_node_size[cur_type];
-loop @+begin r:=r-2;
- v:=value(r);
- type(r):=type(p);
- if r=value(cur_exp) then goto done;
- new_dep(r,copy_dep_list(dep_list(p)));
- dep_mult(r,v,true);
- end;
-done:mem[value_loc(r)]:=mem[value_loc(p)];
-link(prev_dep(p)):=r;
-free_node(p,value_node_size);
-dep_mult(r,v,true);
-end;
-
-@ @<Additional cases of binary operators@>=
-over: if (cur_type<>known)or(type(p)<color_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)+big_node_size[cur_type];
- repeat p:=p-2;
- dep_div(p,v);
- until p=value(cur_exp);
- 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 then
- begin path_trans(c)(p); return;
- end
- else if type(p)=pen_type then
- begin pen_trans(c)(p);
- cur_exp:=convex_hull(cur_exp); {rounding error could destroy convexity}
- 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 do_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 |path_trans(c)(p)| macro applies
-a transformation defined by |cur_exp| and the transform operator |c|
-to the path~|p|.
-
-@d path_trans(#)==begin set_up_known_trans(#); path_trans_end
-@d path_trans_end(#)==unstash_cur_exp(#); do_path_trans(cur_exp); end
-
-@<Declare binary action...@>=
-procedure do_path_trans(@!p:pointer);
-var @!q:pointer; {list traverser}
-begin q:=p;
-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|}
-@^data structure assumptions@>
-q:=link(q);
-until q=p;
-end;
-
-@ Transforming a pen is very similar, except that there are no |left_type|
-and |right_type| fields.
-
-@d pen_trans(#)==begin set_up_known_trans(#); pen_trans_end
-@d pen_trans_end(#)==unstash_cur_exp(#); do_pen_trans(cur_exp); end
-
-@<Declare binary action...@>=
-procedure do_pen_trans(@!p:pointer);
-var @!q:pointer; {list traverser}
-begin if pen_is_elliptical(p) then
- begin trans(p+3,p+4); {that's |left_x| and |left_y|}
- trans(p+5,p+6); {that's |right_x| and |right_y|}
- end;
-q:=p;
-repeat
-trans(q+1,q+2); {that's |x_coord| and |y_coord|}
-@^data structure assumptions@>
-q:=link(q);
-until q=p;
-end;
-
-@ The next transformation procedure applies to edge structures. It will do
-any transformation, but the results may be substandard if the picture contains
-text that uses downloaded bitmap fonts. The binary action procedure is
-|do_edges_trans|, but we also need a function that just scales a picture.
-That routine is |scale_edges|. Both it and the underlying routine |edges_trans|
-should be thought of as procedures that update an edge structure |h|, except
-that they have to return a (possibly new) structure because of the need to call
-|private_edges|.
-
-@<Declare binary action...@>=
-function edges_trans(@!h:pointer):pointer;
-label done1;
-var @!q:pointer; {the object being transformed}
-@!r,@!s:pointer; {for list manipulation}
-@!sx,@!sy:scaled; {saved transformation parameters}
-@!sqdet:scaled; {square root of determinant for |dash_scale|}
-@!sgndet:integer; {sign of the determinant}
-@!v:scaled; {a temporary value}
-begin h:=private_edges(h);@/
-sqdet:=sqrt_det(txx,txy,tyx,tyy);
-sgndet:=ab_vs_cd(txx,tyy,txy,tyx);
-if dash_list(h)<>null_dash then
- @<Try to transform the dash list of |h|@>;
-@<Make the bounding box of |h| unknown if it can't be updated properly
- without scanning the whole structure@>;
-q:=link(dummy_loc(h));
-while q<>null do
- begin @<Transform graphical object |q|@>;@/
- q:=link(q);
- end;
-edges_trans:=h;
-end;
-@#
-procedure do_edges_trans(@!p:pointer;@!c:quarterword);
-begin set_up_known_trans(c);
-value(p):=edges_trans(value(p));
-unstash_cur_exp(p);
-end;
-@#
-procedure scale_edges;
-begin txx:=se_sf; tyy:=se_sf;
-txy:=0; tyx:=0; tx:=0; ty:=0;
-se_pic:=edges_trans(se_pic);
-end;
-
-@ @<Try to transform the dash list of |h|@>=
-if (txy<>0)or(tyx<>0)or(ty<>0)or(abs(txx)<>abs(tyy)) then
- flush_dash_list(h)
-else begin if txx<0 then @<Reverse the dash list of |h|@>;
- @<Scale the dash list by |txx| and shift it by |tx|@>;
- dash_y(h):=take_scaled(dash_y(h),abs(tyy));
- end
-
-@ @<Reverse the dash list of |h|@>=
-begin r:=dash_list(h);
-dash_list(h):=null_dash;
-while r<>null_dash do
- begin s:=r; r:=link(r);@/
- v:=start_x(s); start_x(s):=stop_x(s); stop_x(s):=v;@/
- link(s):=dash_list(h);
- dash_list(h):=s;
- end;
-end
-
-@ @<Scale the dash list by |txx| and shift it by |tx|@>=
-r:=dash_list(h);
-while r<>null_dash do
- begin start_x(r):=take_scaled(start_x(r),txx)+tx;
- stop_x(r):=take_scaled(stop_x(r),txx)+tx;@/
- r:=link(r);
- end
-
-@ @<Make the bounding box of |h| unknown if it can't be updated properly...@>=
-if (txx=0)and(tyy=0) then
- @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>
-else if (txy<>0)or(tyx<>0) then
- begin init_bbox(h);
- goto done1;
- end;
-if minx_val(h)<=maxx_val(h) then
- @<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift by
- |(tx,ty)|@>;
-done1:
-
-@ @<Swap the $x$ and $y$ parameters in the bounding box of |h|@>=
-begin v:=minx_val(h); minx_val(h):=miny_val(h); miny_val(h):=v;@/
-v:=maxx_val(h); maxx_val(h):=maxy_val(h); maxy_val(h):=v;
-end
-
-@ The sum ``|txx+txy|'' is whichever of |txx| or |txy| is nonzero. The other
-sum is similar.
-
-@<Scale the bounding box by |txx+txy| and |tyx+tyy|; then shift...@>=
-begin minx_val(h):=take_scaled(minx_val(h),txx+txy)+tx;@/
-maxx_val(h):=take_scaled(maxx_val(h),txx+txy)+tx;@/
-miny_val(h):=take_scaled(miny_val(h),tyx+tyy)+ty;@/
-maxy_val(h):=take_scaled(maxy_val(h),tyx+tyy)+ty;@/
-if txx+txy<0 then
- begin v:=minx_val(h); minx_val(h):=maxx_val(h); maxx_val(h):=v;
- end;
-if tyx+tyy<0 then
- begin v:=miny_val(h); miny_val(h):=maxy_val(h); maxy_val(h):=v;
- end;
-end
-
-@ Now we ready for the main task of transforming the graphical objects in edge
-structure~|h|.
-
-@<Transform graphical object |q|@>=
-case type(q) of
-fill_code,stroked_code: begin
- do_path_trans(path_p(q));
- @<Transform |pen_p(q)|, making sure polygonal pens stay counter-clockwise@>;
- end;
-start_clip_code,start_bounds_code: do_path_trans(path_p(q));
-text_code:begin r:=text_tx_loc(q);
- @<Transform the compact transformation starting at |r|@>;
- end;
-stop_clip_code,stop_bounds_code: do_nothing;
-end {there are no other cases}
-
-@ Note that the shift parameters |(tx,ty)| apply only to the path being stroked.
-The |dash_scale| has to be adjusted to scale the dash lengths in |dash_p(q)|
-since the \ps\ output procedures will try to compensate for the transformation
-we are applying to |pen_p(q)|. Since this compensation is based on the square
-root of the determinant, |sqdet| is the appropriate factor.
-
-@<Transform |pen_p(q)|, making sure...@>=
-if pen_p(q)<>null then
- begin sx:=tx; sy:=ty;
- tx:=0; ty:=0;@/
- do_pen_trans(pen_p(q));
- if ((type(q)=stroked_code)and(dash_p(q)<>null)) then
- dash_scale(q):=take_scaled(dash_scale(q),sqdet);
- if not pen_is_elliptical(pen_p(q)) then
- if sgndet<0 then
- pen_p(q):=make_pen(copy_path(pen_p(q)),true); {this unreverses the pen}
- tx:=sx; ty:=sy;
- end
-
-@ This uses the fact that transformations are stored in the order
-|(tx,ty,txx,txy,tyx,tyy)|.
-@^data structure assumptions@>
-
-@<Transform the compact transformation starting at |r|@>=
-trans(r,r+1);
-sx:=tx; sy:=ty;
-tx:=0; ty:=0;
-trans(r+2,r+4);
-trans(r+3,r+5);
-tx:=sx; ty:=sy
-
-@ 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_stop(a)-1 do append_char(so(str_pool[k]));
-for k:=str_start[b] to str_stop(b)-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); 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);
- 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 split_cubic(q,a*@'10000); q:=link(q);
- end;
-pp:=copy_knot(q); qq:=pp;
-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: if (cur_type=pen_type)and nice_pair(p,type(p)) then
- set_up_offset(value(p))
- else bad_binary(p,pen_offset_of);
-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}
-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 split_cubic(p,v*@'10000); 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 binary operators@>=
-arc_time_of: begin if cur_type=pair_type then
- pair_to_path;
- if (cur_type=path_type)and(type(p)=known) then
- flush_cur_exp(get_arc_time(cur_exp,value(p)))
- else bad_binary(p,c);
- end;
-
-@ @<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;
-
-@ @<Additional cases of bin...@>=
-in_font:if (cur_type<>string_type)or(type(p)<>string_type)
- then bad_binary(p,in_font)
- else begin do_infont(p); return;
- end;
-
-@ Function |new_text_node| owns the reference count for its second argument
-(the text string) but not its first (the font name).
-
-@<Declare binary action...@>=
-procedure do_infont(@!p:pointer);
-var @!q:pointer;
-begin q:=get_node(edge_header_size);
-init_edges(q);
-link(obj_tail(q)):=new_text_node(cur_exp,value(p));
-obj_tail(q):=link(obj_tail(q));
-free_node(p,value_node_size);@/
-flush_cur_exp(q);
-cur_type:=picture_type;
-end;
-
-@* \[40] Statements and commands.
-The chief executive of \MP\ is the |do_statement| routine, which
-contains the master switch that causes all the various pieces of \MP\
-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 @<Declare action procedures for use by |do_statement|@>@;
-procedure do_statement; {governs \MP'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_t_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(""); print(cur_exp); update_terminal;
- end;
-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 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...@>
- 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); v:=0;
-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,color_type,cmykcolor_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("color",type_name,color_type);@/
-@!@:color_}{\&{color} primitive@>
-primitive("rgbcolor",type_name,color_type);@/
-@!@:color_}{\&{rgbcolor} primitive@>
-primitive("cmykcolor",type_name,cmykcolor_type);@/
-@!@:color_}{\&{cmykcolor} 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_t_next;
-@<Decrease the string reference count...@>;
-until cur_cmd>=comma; {either |end_of_statement| or |cur_cmd=comma|}
-scanner_status:=normal;
-end
-
-@ \MP'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");
-
-@* \[41] 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;
-
-@ \MP\ never defines the tokens `\.(' and `\.)' to be primitives, but
-plain \MP\ 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 \MP\ 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 `"); 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 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);
-@:MetaPost 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 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(""""); 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 "); 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:print(int_name[m]);
-
-@ @<Declare action procedures for use by |do_statement|@>=
-procedure do_show_token;
-begin repeat get_t_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 ");
-stat print_int(strs_in_use-init_str_use);
-print_char("&"); print_int(pool_in_use-init_pool_ptr);
-if false then@+tats@t@>@;@/
-print("unknown");
-print(" (");
-print_int(max_strings-1-strs_used_up); print_char("&");
-print_int(pool_size-pool_ptr); print(" now 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_t_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 double_path_code=0 {command modifier for `\&{doublepath}'}
-@d contour_code=1 {command modifier for `\&{contour}'}
-@d also_code=2 {command modifier for `\&{also}'}
-
-@ Pre and postscripts need two new identifiers:
-
-@d with_pre_script=11
-@d with_post_script=13
-
-@<Put each...@>=
-primitive("doublepath",thing_to_add,double_path_code);@/
-@!@:double_path_}{\&{doublepath} primitive@>
-primitive("contour",thing_to_add,contour_code);@/
-@!@:contour_}{\&{contour} primitive@>
-primitive("also",thing_to_add,also_code);@/
-@!@:also_}{\&{also} primitive@>
-primitive("withpen",with_option,pen_type);@/
-@!@:with_pen_}{\&{withpen} primitive@>
-primitive("dashed",with_option,picture_type);@/
-@!@:dashed_}{\&{dashed} primitive@>
-primitive("withprescript",with_option,with_pre_script);@/
-@!@:with_pre_script_}{\&{withprescript} primitive@>
-primitive("withpostscript",with_option,with_post_script);@/
-@!@:with_post_script_}{\&{withpostscript} primitive@>
-primitive("withoutcolor",with_option,no_model);@/
-@!@:with_color_}{\&{withoutcolor} primitive@>
-primitive("withgreyscale",with_option,grey_model);@/
-@!@:with_color_}{\&{withgreyscale} primitive@>
-primitive("withcolor",with_option,uninitialized_model);@/
-@!@:with_color_}{\&{withcolor} primitive@>
-{ \&{withrgbcolor} is an alias for \&{withcolor}}
-primitive("withrgbcolor",with_option,rgb_model);@/
-@!@:with_color_}{\&{withrgbcolor} primitive@>
-primitive("withcmykcolor",with_option,cmyk_model);@/
-@!@:with_color_}{\&{withcmykcolor} 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 if m=with_pre_script then print("withprescript")
- else if m=with_post_script then print("withpostscript")
- else if m=no_model then print("withoutcolor")
- else if m=rgb_model then print("withrgbcolor")
- else if m=uninitialized_model then print("withcolor")
- else if m=cmyk_model then print("withcmykcolor")
- else if m=grey_model then print("withgreyscale")
- else print("dashed");
-
-@ The |scan_with_list| procedure parses a $\langle$with list$\rangle$ and
-updates the list of graphical objects starting at |p|. Each $\langle$with
-clause$\rangle$ updates all graphical objects whose |type| is compatible.
-Other objects are ignored.
-
-@<Declare action procedures for use by |do_statement|@>=
-procedure scan_with_list(@!p:pointer);
-label done, done1, done2;
-var @!t:small_number; {|cur_mod| of the |with_option| (should match |cur_type|)}
-@!q:pointer; {for list manipulation}
-@!old_setting:0..max_selector; {saved |selector| setting}
-@!k:pointer; {for finding the near-last item in a list }
-@!s:str_number; {for string cleanup after combining }
-@!cp,@!pp,@!dp,@!ap,@!bp:pointer;
- {objects being updated; |void| initially; |null| to suppress update}
-begin cp:=void; pp:=void; dp:=void; ap:=void; bp:=void; k:=null;
-while cur_cmd=with_option do
- begin t:=cur_mod;
- get_x_next;
- if t<>no_model then scan_expression;
- if ((t=with_pre_script)and(cur_type<>string_type))or
- ((t=with_post_script)and(cur_type<>string_type))or
- ((t=uninitialized_model)and
- ((cur_type<>cmykcolor_type)and(cur_type<>color_type)
- and(cur_type<>known)and(cur_type<>boolean_type)))or
- ((t=cmyk_model)and(cur_type<>cmykcolor_type))or
- ((t=rgb_model)and(cur_type<>color_type))or
- ((t=grey_model)and(cur_type<>known))or
- ((t=pen_type)and(cur_type<>t))or
- ((t=picture_type)and(cur_type<>t)) then @<Complain about improper type@>
- else if t=uninitialized_model then
- begin if cp=void then @<Make |cp| a colored object in object list~|p|@>;
- if cp<>null then
- @<Transfer a color from the current expression to object~|cp|@>;
- flush_cur_exp(0);
- end
- else if t=rgb_model then
- begin if cp=void then @<Make |cp| a rgb colored object in object list~|p|@>;
- if cp<>null then
- @<Transfer a rgbcolor from the current expression to object~|cp|@>;
- flush_cur_exp(0);
- end
- else if t=cmyk_model then
- begin if cp=void then @<Make |cp| a cmyk colored object in object list~|p|@>;
- if cp<>null then
- @<Transfer a cmykcolor from the current expression to object~|cp|@>;
- flush_cur_exp(0);
- end
- else if t=grey_model then
- begin if cp=void then @<Make |cp| a grey colored object in object list~|p|@>;
- if cp<>null then
- @<Transfer a greyscale from the current expression to object~|cp|@>;
- flush_cur_exp(0);
- end
- else if t=no_model then
- begin if cp=void then @<Make |cp| a no colored object in object list~|p|@>;
- if cp<>null then
- @<Transfer a noncolor from the current expression to object~|cp|@>;
- end
- else if t=pen_type then
- begin if pp=void then @<Make |pp| an object in list~|p| that needs
- a pen@>;
- if pp<>null then
- begin if pen_p(pp)<>null then toss_knot_list(pen_p(pp));
- pen_p(pp):=cur_exp; cur_type:=vacuous;
- end;
- end
- else if t=with_pre_script then
- begin if ap=void then
- ap:=p;
- while (ap<>null)and(not has_color(ap)) do
- ap:=link(ap);
- if ap<>null then
- begin if pre_script(ap)<>null then begin { build a new,combined string }
- s:=pre_script(ap);
- old_setting:=selector;
- selector:=new_string;
- str_room(length(pre_script(ap))+length(cur_exp)+2);
- print(cur_exp);
- append_char(13); {a forced \ps\ newline }
- print(pre_script(ap));
- pre_script(ap):=make_string;
- delete_str_ref(s);
- selector:=old_setting;
- end
- else
- pre_script(ap):=cur_exp;
- cur_type:=vacuous;
- end;
- end
- else if t=with_post_script then
- begin if bp=void then
- k:=p; bp:=k;
- while link(k)<>null do begin
- k:=link(k);
- if has_color(k) then bp:=k;
- end;
- if bp<>null then
- begin if post_script(bp)<>null then begin
- s:=post_script(bp);
- old_setting:=selector;
- selector:=new_string;
- str_room(length(post_script(bp))+length(cur_exp)+2);
- print(post_script(bp));
- append_char(13); {a forced \ps\ newline }
- print(cur_exp);
- post_script(bp):=make_string;
- delete_str_ref(s);
- selector:=old_setting;
- end
- else
- post_script(bp):=cur_exp;
- cur_type:=vacuous;
- end;
- end
- else begin if dp=void then @<Make |dp| a stroked node in list~|p|@>;
- if dp<>null then
- begin if dash_p(dp)<>null then delete_edge_ref(dash_p(dp));
- dash_p(dp):=make_dashes(cur_exp);
- dash_scale(dp):=unity;
- cur_type:=vacuous;
- end;
- end;
- end;
- @<Copy the information from objects |cp|, |pp|, and |dp| into the rest
- of the list@>;
-end;
-
-@ @<Complain about improper type@>=
-begin exp_err("Improper type");
-@.Improper type@>
-help2("Next time say `withpen <known pen expression>';")@/
- ("I'll ignore the bad `with' clause and look for another.");
-if t=with_pre_script then
- help_line[1]:="Next time say `withprescript <known string expression>';"
-else if t=with_post_script then
- help_line[1]:="Next time say `withpostscript <known string expression>';"
-else if t=picture_type then
- help_line[1]:="Next time say `dashed <known picture expression>';"
-else if t=uninitialized_model then
- help_line[1]:="Next time say `withcolor <known color expression>';"
-else if t=rgb_model then
- help_line[1]:="Next time say `withrgbcolor <known color expression>';"
-else if t=cmyk_model then
- help_line[1]:="Next time say `withcmykcolor <known cmykcolor expression>';"
-else if t=grey_model then
- help_line[1]:="Next time say `withgreyscale <known numeric expression>';";
-put_get_flush_error(0);
-end
-
-@ Forcing the color to be between |0| and |unity| here guarantees that no
-picture will ever contain a color outside the legal range for \ps\ graphics.
-
-@<Transfer a color from the current expression to object~|cp|@>=
-begin if cur_type=color_type then
- @<Transfer a rgbcolor from the current expression to object~|cp|@>
-else if cur_type=cmykcolor_type then
- @<Transfer a cmykcolor from the current expression to object~|cp|@>
-else if cur_type=known then
- @<Transfer a greyscale from the current expression to object~|cp|@>
-else if cur_exp=false_code then
- @<Transfer a noncolor from the current expression to object~|cp|@>;
-end
-
-@ @<Transfer a rgbcolor from the current expression to object~|cp|@>=
-begin q:=value(cur_exp);
-cyan_val(cp):=0;
-magenta_val(cp):=0;
-yellow_val(cp):=0;
-black_val(cp):=0;
-red_val(cp):=value(red_part_loc(q));
-green_val(cp):=value(green_part_loc(q));
-blue_val(cp):=value(blue_part_loc(q));@/
-color_model(cp):=rgb_model;
-if red_val(cp)<0 then red_val(cp):=0;
-if green_val(cp)<0 then green_val(cp):=0;
-if blue_val(cp)<0 then blue_val(cp):=0;
-if red_val(cp)>unity then red_val(cp):=unity;
-if green_val(cp)>unity then green_val(cp):=unity;
-if blue_val(cp)>unity then blue_val(cp):=unity;
-end
-
-@ @<Transfer a cmykcolor from the current expression to object~|cp|@>=
-begin q:=value(cur_exp);
-cyan_val(cp):=value(cyan_part_loc(q));
-magenta_val(cp):=value(magenta_part_loc(q));
-yellow_val(cp):=value(yellow_part_loc(q));@/
-black_val(cp):=value(black_part_loc(q));@/
-color_model(cp):=cmyk_model;
-if cyan_val(cp)<0 then cyan_val(cp):=0;
-if magenta_val(cp)<0 then magenta_val(cp):=0;
-if yellow_val(cp)<0 then yellow_val(cp):=0;
-if black_val(cp)<0 then black_val(cp):=0;
-if cyan_val(cp)>unity then cyan_val(cp):=unity;
-if magenta_val(cp)>unity then magenta_val(cp):=unity;
-if yellow_val(cp)>unity then yellow_val(cp):=unity;
-if black_val(cp)>unity then black_val(cp):=unity;
-end
-
-@ @<Transfer a greyscale from the current expression to object~|cp|@>=
-begin q:=cur_exp;
-cyan_val(cp):=0;
-magenta_val(cp):=0;
-yellow_val(cp):=0;
-black_val(cp):=0;
-grey_val(cp):=q;
-color_model(cp):=grey_model;
-if grey_val(cp)<0 then grey_val(cp):=0;
-if grey_val(cp)>unity then grey_val(cp):=unity;
-end
-
-@ @<Transfer a noncolor from the current expression to object~|cp|@>=
-begin
-cyan_val(cp):=0;
-magenta_val(cp):=0;
-yellow_val(cp):=0;
-black_val(cp):=0;
-grey_val(cp):=0;
-color_model(cp):=no_model;
-end
-
-@ It is a bit silly to repeat this action in five different
-forms with the only difference being the label. Ah well.
-
-@<Make |cp| a colored object in object list~|p|@>=
-begin cp:=p;
-while cp<>null do
- begin if has_color(cp) then goto done;
- cp:=link(cp);
- end;
-done:do_nothing;
-end
-
-@ @<Make |cp| a rgb colored object in object list~|p|@>=
-begin cp:=p;
-while cp<>null do
- begin if has_color(cp) then goto done6;
- cp:=link(cp);
- end;
-done6:do_nothing;
-end
-
-@ @<Make |cp| a cmyk colored object in object list~|p|@>=
-begin cp:=p;
-while cp<>null do
- begin if has_color(cp) then goto done5;
- cp:=link(cp);
- end;
-done5:do_nothing;
-end
-
-@ @<Make |cp| a grey colored object in object list~|p|@>=
-begin cp:=p;
-while cp<>null do
- begin if has_color(cp) then goto done4;
- cp:=link(cp);
- end;
-done4:do_nothing;
-end
-
-@ @<Make |cp| a no colored object in object list~|p|@>=
-begin cp:=p;
-while cp<>null do
- begin if has_color(cp) then goto done3;
- cp:=link(cp);
- end;
-done3:do_nothing;
-end
-
-@ @<Make |pp| an object in list~|p| that needs a pen@>=
-begin pp:=p;
-while pp<>null do
- begin if has_pen(pp) then goto done1;
- pp:=link(pp);
- end;
-done1:do_nothing;
-end
-
-@ @<Make |dp| a stroked node in list~|p|@>=
-begin dp:=p;
-while dp<>null do
- begin if type(dp)=stroked_code then goto done2;
- dp:=link(dp);
- end;
-done2:do_nothing;
-end
-
-@ @<Copy the information from objects |cp|, |pp|, and |dp| into...@>=
-@<Copy |cp|'s color into the colored objects linked to~|cp|@>;
-if pp>void then
- @<Copy |pen_p(pp)| into stroked and filled nodes linked to |pp|@>;
-if dp>void then @<Make stroked nodes linked to |dp| refer to |dash_p(dp)|@>
-
-@ @<Copy |cp|'s color into the colored objects linked to~|cp|@>=
-begin q:=link(cp);
-while q<>null do
- begin if has_color(q) then
- begin red_val(q):=red_val(cp);
- green_val(q):=green_val(cp);
- blue_val(q):=blue_val(cp);@/
- black_val(q):=black_val(cp);@/
- color_model(q):=color_model(cp);@/
- end;
- q:=link(q);
- end;
-end
-
-@ @<Copy |pen_p(pp)| into stroked and filled nodes linked to |pp|@>=
-begin q:=link(pp);
-while q<>null do
- begin if has_pen(q) then
- begin if pen_p(q)<>null then toss_knot_list(pen_p(q));
- pen_p(q):=copy_pen(pen_p(pp));
- end;
- q:=link(q);
- end;
-end
-
-@ @<Make stroked nodes linked to |dp| refer to |dash_p(dp)|@>=
-begin q:=link(dp);
-while q<>null do
- begin if type(q)=stroked_code then
- begin if dash_p(q)<>null then delete_edge_ref(dash_p(q));
- dash_p(q):=dash_p(dp);
- dash_scale(q):=unity;
- if dash_p(q)<>null then add_edge_ref(dash_p(q));
- end;
- q:=link(q);
- end;
-end
-
-@ One of the things we need to do when we've parsed an \&{addto} or
-similar command is find the header of a supposed \&{picture} variable, given
-a token list for that variable. Since the edge structure is about to be
-updated, we use |private_edges| to make sure that this is possible.
-
-@<Declare action procedures for use by |do_statement|@>=
-function find_edges_var(@!t:pointer):pointer;
-var @!p:pointer;
-@!cur_edges:pointer; {the return value}
-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 begin value(p):=private_edges(value(p));
- cur_edges:=value(p);
- end;
-flush_node_list(t);
-find_edges_var:=cur_edges;
-end;
-
-@ @<Cases of |do_statement|...@>=
-add_to_command: do_add_to;
-bounds_command:do_bounds;
-
-@ @<Put each...@>=
-primitive("clip",bounds_command,start_clip_code);@/
-@!@:clip_}{\&{clip} primitive@>
-primitive("setbounds",bounds_command,start_bounds_code);@/
-@!@:set_bounds_}{\&{setbounds} primitive@>
-
-@ @<Cases of |print_cmd...@>=
-bounds_command: if m=start_clip_code then print("clip")
- else print("setbounds");
-
-@ The following function parses the beginning of an \&{addto} or \&{clip}
-command: it expects a variable name followed by a token with |cur_cmd=sep|
-and then an expression. The function returns the token list for the variable
-and stores the command modifier for the separator token in the global variable
-|last_add_type|. We must be careful because this variable might get overwritten
-any time we call |get_x_next|.
-
-@<Glob...@>=
-@!last_add_type:quarterword;
- {command modifier that identifies the last \&{addto} command}
-
-@ @<Declare action procedures for use by |do_statement|@>=
-function start_draw_cmd(@!sep:quarterword):pointer;
-var @!lhv:pointer; {variable to add to left}
-@!add_type:quarterword; {value to be returned in |last_add_type|}
-begin lhv:=null; add_type:=0;
-get_x_next; var_flag:=sep; scan_primary;
-if cur_type<>token_list then
- @<Abandon edges command because there's no variable@>
-else begin lhv:=cur_exp; add_type:=cur_mod;@/
- cur_type:=vacuous; get_x_next; scan_expression;
- end;
-last_add_type:=add_type;
-start_draw_cmd:=lhv;
-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
-
-@ Here is an example of how to use |start_draw_cmd|.
-
-@<Declare action procedures for use by |do_statement|@>=
-procedure do_bounds;
-var @!lhv,@!lhe:pointer; {variable on left, the corresponding edge structure}
-@!p:pointer; {for list manipulation}
-@!m:integer; {initial value of |cur_mod|}
-begin m:=cur_mod;
-lhv:=start_draw_cmd(to_token);@/
-if lhv<>null then
- begin lhe:=find_edges_var(lhv);
- if lhe=null then flush_cur_exp(0)
- else if cur_type<>path_type then
- begin exp_err("Improper `clip'");
-@.Improper `addto'@>
- help2("This expression should have specified a known path.")@/
- ("So I'll not change anything just now."); put_get_flush_error(0);
- end
- else if left_type(cur_exp)=endpoint then @<Complain about a non-cycle@>
- else @<Make |cur_exp| into a \&{setbounds} or clipping path and add
- it to |lhe|@>;
- end;
-end;
-
-@ @<Complain about a 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;
-end
-
-@ @<Make |cur_exp| into a \&{setbounds} or clipping path and add...@>=
-begin p:=new_bounds_node(cur_exp,m);
-link(p):=link(dummy_loc(lhe));
-link(dummy_loc(lhe)):=p;@/
-if obj_tail(lhe)=dummy_loc(lhe) then obj_tail(lhe):=p;
-p:=get_node(gr_object_size[stop_type(m)]);
-type(p):=stop_type(m);
-link(obj_tail(lhe)):=p;
-obj_tail(lhe):=p;@/
-init_bbox(lhe);
-end
-
-@ The |do_add_to| procedure is a little like |do_clip| but there are a lot more
-cases to deal with.
-
-@<Declare action procedures for use by |do_statement|@>=
-procedure do_add_to;
-var @!lhv,@!lhe:pointer; {variable on left, the corresponding edge structure}
-@!p:pointer; {the graphical object or list for |scan_with_list| to update}
-@!e:pointer; {an edge structure to be merged}
-@!add_type:quarterword; {|also_code|, |contour_code|, or |double_path_code|}
-begin lhv:=start_draw_cmd(thing_to_add); add_type:=last_add_type;@/
-if lhv<>null then
- begin if add_type=also_code then
- @<Make sure the current expression is a suitable picture and set |e| and |p|
- appropriately@>
- else @<Create a graphical object |p| based on |add_type| and the current
- expression@>;
- scan_with_list(p);
- @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>;
- end;
-end;
-
-@ Setting |p:=null| causes the $\langle$with list$\rangle$ to be ignored;
-setting |e:=null| prevents anything from being added to |lhe|.
-
-@ @<Make sure the current expression is a suitable picture and set |e|...@>=
-begin p:=null; e:=null;
-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 e:=private_edges(cur_exp); cur_type:=vacuous;
- p:=link(dummy_loc(e));
- end;
-end
-
-@ In this case |add_type<>also_code| so setting |p:=null| suppresses future
-attempts to add to the edge structure.
-
-@<Create a graphical object |p| based on |add_type| and the current...@>=
-begin e:=null; p:=null;
-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 specified a known path.")@/
- ("So I'll not change anything just now."); put_get_flush_error(0);
- end
-else if add_type=contour_code then
- if left_type(cur_exp)=endpoint then
- @<Complain about a non-cycle@>
- else begin p:=new_fill_node(cur_exp);
- cur_type:=vacuous;
- end
- else begin p:=new_stroked_node(cur_exp);
- cur_type:=vacuous;
- end;
-end
-
-@ @<Use |p|, |e|, and |add_type| to augment |lhv| as requested@>=
-lhe:=find_edges_var(lhv);
-if lhe=null then
- begin if (e=null)and(p<>null) then e:=toss_gr_object(p);
- if e<>null then delete_edge_ref(e);
- end
-else if add_type=also_code then
- if e<>null then @<Merge |e| into |lhe| and delete |e|@>
- else do_nothing
-else if p<>null then
- begin link(obj_tail(lhe)):=p;
- obj_tail(lhe):=p;
- if add_type=double_path_code then
- if pen_p(p)=null then pen_p(p):=get_pen_circle(0);
- end
-
-@ @<Merge |e| into |lhe| and delete |e|@>=
-begin if link(dummy_loc(e))<>null then
- begin link(obj_tail(lhe)):=link(dummy_loc(e));
- obj_tail(lhe):=obj_tail(e);@/
- obj_tail(e):=dummy_loc(e);
- link(dummy_loc(e)):=null;
- flush_dash_list(lhe);
- end;
-toss_edges(e);
-end
-
-@ @<Cases of |do_statement|...@>=
-ship_out_command: do_ship_out;
-
-@ @<Declare action procedures for use by |do_statement|@>=
-@t\4@>@<Declare the function called |tfm_check|@>@;
-@t\4@>@<Declare the \ps\ output procedures@>@;
-procedure do_ship_out;
-var @!c:integer; {the character code}
-begin get_x_next; scan_expression;
-if cur_type<>picture_type then
- @<Complain that it's not a known picture@>
-else begin c:=round_unscaled(internal[char_code]) mod 256;
- if c<0 then c:=c+256;
- @<Store the width information for character code~|c|@>;@/
- ship_out(cur_exp);
- flush_cur_exp(0);
- end;
-end;
-
-@ @<Complain that it's not a known picture@>=
-begin exp_err("Not a known picture");
-help1("I can only output known pictures.");
-put_get_flush_error(0);
-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
-@d filename_template_code=3
-@d print_with_leading_zeroes(#)== g := pool_ptr;
- print_int(#); g := pool_ptr-g;
- if f>g then begin
- pool_ptr := pool_ptr - g;
- while f>g do begin
- print_char("0");
- decr(f);
- end;
- print_int(#);
- end;
- f := 0
-
-@<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@>
-primitive("filenametemplate",message_command,filename_template_code);@/
-@!@:filename_template_}{\&{filenametemplate} 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 if m=filename_template_code then print("filenametemplate")
- else print("errhelp");
-
-@ @<Cases of |do_statement|...@>=
-message_command: do_message;
-
-@ @<Declare action procedures for use by |do_statement|@>=
-@<Declare a procedure called |no_string_err|@>@;
-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
- no_string_err("A message should be a known string expression.")
-else case m of
- message_code:begin print_nl(""); 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|@>;
- filename_template_code:@<Save the filename template@>;
- end; {there are no other cases}
-flush_cur_exp(0);
-end;
-
-@ @<Declare a procedure called |no_string_err|@>=
-procedure no_string_err(s:str_number);
-begin exp_err("Not a string");
-@.Not a string@>
-help1(s);
-put_get_error;
-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(""); 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
-
-@ @<Cases of |do_statement|...@>=
-write_command: do_write;
-
-@ @<Declare action procedures for use by |do_statement|@>=
-procedure do_write;
-label continue;
-var @!t:str_number; {the line of text to be written}
- @!n,@!n0:write_index; {for searching |wr_fname| and |wr_file| arrays}
- @!old_setting:0..max_selector; {for saving |selector| during output}
-begin get_x_next;
-scan_expression;
-if cur_type<>string_type then
- no_string_err("The text to be written should be a known string expression")
-else if cur_cmd<>to_token then
- begin print_err("Missing `to' clause");
- help1("A write command should end with `to <filename>'");
- put_get_error;
- end
-else begin t:=cur_exp; cur_type:=vacuous;
- get_x_next;
- scan_expression;
- if cur_type<>string_type then
- no_string_err("I can't write to that file name. It isn't a known string")
- else @<Write |t| to the file named by |cur_exp|@>;
- delete_str_ref(t);
- end;
-flush_cur_exp(0);
-end;
-
-@ @<Write |t| to the file named by |cur_exp|@>=
-begin @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if
- |cur_exp| must be inserted@>;
-@<Make sure |eof_line| is initialized@>;
-if str_vs_str(t,eof_line)=0 then
- @<Record the end of file on |wr_file[n]|@>
-else begin old_setting:=selector;
- selector:=n;
- print(t); print_ln;
- selector := old_setting;
- end;
-end
-
-@ @<Find |n| where |wr_fname[n]=cur_exp| and call |open_write_file| if...@>=
-n:=write_files;
-n0:=write_files;
-repeat
-continue:if n=0 then
- @<Insert |cur_exp| at index |n0| and call |open_write_file|@>
-else begin decr(n);
- if wr_fname[n]=0 then
- begin n0:=n; goto continue;
- end;
- end;
-until str_vs_str(cur_exp,wr_fname[n])=0
-
-@ @<Insert |cur_exp| at index |n0| and call |open_write_file|@>=
-begin if n0=write_files then
- if write_files<max_write_files then incr(write_files)
- else overflow("write files",max_write_files);
-n:=n0;
-open_write_file(cur_exp,n);
-end
-
-@ @<Record the end of file on |wr_file[n]|@>=
-begin a_close(wr_file[n]);
-delete_str_ref(wr_fname[n]);
-wr_fname[n]:=0;
-if n=write_files-1 then write_files:=n;
-end
-
-
-@* \[42] Writing 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 \MP'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 \MP\ 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 \MP\ 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 \MP\ 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.
-
-\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 location |remainder| in the |lig_kern| array.\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
-\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 \MP'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;
-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 \MP'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);
-@:MetaPost 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);
-@:MetaPost 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);
-@:MetaPost 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);
-@:MetaPost 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);
-@:MetaPost 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.
-
-\MP\ 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 \MP'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+halfp(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 \MP\ 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 "); 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
-
-@* \[43] Reading font metric data.
-
-\MP\ isn't a typesetting program but it does need to find the bounding box
-of a sequence of typeset characters. Thus it needs to read \.{TFM} files as
-well as write them.
-
-@<Glob...@>=
-tfm_infile:byte_file;
-
-@ All the width, height, and depth information is stored in an array called
-|font_info|. This array is allocated sequentially and each font is stored
-as a series of |char_info| words followed by the width, height, and depth
-tables. Since |font_name| entries are permanent, their |str_ref| values are
-set to |max_str_ref|.
-
-@<Types...@>=
-font_number=0..font_max;
-
-@ @<Glob...@>=
-font_info:array[0..font_mem_size] of memory_word;
- {height, width, and depth data}
-@!font_enc_name:array[font_number] of str_number;
- {encoding names, if any}
-@!font_ps_name_fixed:array[font_number] of boolean;
- {are the postscript names fixed already? }
-@!mp_font_map: ^fm_entry_ptr; {pointer into AVL tree of font mappings}
-next_fmem:0..font_mem_size; {next unused entry in |font_info|}
-last_fnum:font_number; {last font number used so far}
-font_dsize:array[font_number] of scaled;
- {16 times the ``design'' size in \ps\ points}
-font_name:array[font_number] of str_number;
- {name as specified in the \&{infont} command}
-font_ps_name:array[font_number] of str_number;
- {PostScript name for use when |internal[prologues]>0|}
-last_ps_fnum:font_number; {last valid |font_ps_name| index}
-font_bc,font_ec:array[font_number] of eight_bits;
- {first and last character code}
-
-@ The |font_info| array is indexed via a group directory arrays.
-For example, the |char_info| data for character~|c| in font~|f| will be
-in |font_info[char_base[f]+c].qqqq|.
-
-@<Glob...@>=
-char_base:array[font_number] of 0..font_mem_size;
- {base address for |char_info|}
-width_base:array[font_number] of 0..font_mem_size;
- {index for zeroth character width}
-height_base:array[font_number] of 0..font_mem_size;
- {index for zeroth character height}
-depth_base:array[font_number] of 0..font_mem_size;
- {index for zeroth character depth}
-
-@ A |null_font| containing no characters is useful for error recovery. Its
-|font_name| entry starts out empty but is reset each time an erroneous font is
-found. This helps to cut down on the number of duplicate error messages without
-wasting a lot of space.
-
-@d null_font=0 {the |font_number| for an empty font}
-
-@<Initialize table...@>=
-font_dsize[null_font]:=0;
-font_name[null_font]:="";
-font_ps_name[null_font]:="";
-font_bc[null_font]:=1;
-font_ec[null_font]:=0;@/
-char_base[null_font]:=0;
-width_base[null_font]:=0;
-height_base[null_font]:=0;
-depth_base[null_font]:=0;@/
-next_fmem:=0;
-last_fnum:=null_font;
-last_ps_fnum:=null_font;
-
-@ Each |char_info| word is of type |four_quarters|. The |b0| field contains
-|min_quarter_word| plus the |width index|; the |b1| field contains the height
-index; the |b2| fields contains the depth index, and the |b3| field used only
-for temporary storage. (It is used to keep track of which characters occur in
-an edge structure that is being shipped out.)
-The corresponding words in the width, height, and depth tables are stored as
-|scaled| values in units of \ps\ points.
-
-With the macros below, the |char_info| word for character~|c| in font~|f| is
-|char_info(f)(c)| and the width is
-$$\hbox{|char_width(f)(char_info(f)(c)).sc|.}$$
-
-@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_height_end(#)==#.b1].sc
-@d char_height(#)==font_info[height_base[#]+char_height_end
-@d char_depth_end(#)==#.b2].sc
-@d char_depth(#)==font_info[depth_base[#]+char_depth_end
-@d ichar_exists(#)==(#.b0>min_quarterword)
-
-@ The |font_ps_name| for a built-in font should be what PostScript expects.
-A preliminary name is obtained here from the \.{TFM} name as given in the
-|fname| argument. This gets updated later from an external table if necessary.
-
-@d bad_tfm=11 {go here if the \.{TFM} file is bad}
-
-@<Declare text measuring subroutines@>=
-@<Declare subroutines for parsing file names@>@;
-function read_font_info(fname:str_number):font_number;
-label bad_tfm,done;
-var @!file_opened:boolean; {has |tfm_infile| been opened?}
-@!n:font_number; {the number to return}
-@!lf,@!lh,@!bc,@!ec,@!nw,@!nh,@!nd:halfword; {subfile size parameters}
-@!whd_size:integer; {words needed for heights, widths, and depths}
-@!i,@!ii:0..font_mem_size; {|font_info| indices}
-@!jj:0..font_mem_size; {counts bytes to be ignored}
-@!z:scaled; {used to compute the design size}
-@!d:fraction;
- {height, width, or depth as a fraction of design size times $2^{-8}$}
-@!h_and_d:eight_bits; {height and depth indices being unpacked}
-begin n:=null_font;
-@<Open |tfm_infile| for input@>;
-@<Read data from |tfm_infile|; if there is no room, say so and |goto done|;
- otherwise |goto bad_tfm| or |goto done| as appropriate@>;
-bad_tfm:@<Complain that the \.{TFM} file is bad@>;
-done:if file_opened then b_close(tfm_infile);
-if n<>null_font then
- begin font_ps_name[n]:=fname;
- font_name[n]:=fname;
- str_ref[fname]:=max_str_ref;
- end;
-read_font_info:=n;
-end;
-
-@ \MP\ doesn't bother to check the entire \.{TFM} file for errors or explain
-precisely what is wrong if it does find a problem. Programs called \.{TFtoPL}
-@.TFtoPL@> @.PLtoTF@>
-and \.{PLtoTF} can be used to debug \.{TFM} files.
-
-@<Complain that the \.{TFM} file is bad@>=
-print_err("Font ");
-print(fname);
-if file_opened then print(" not usable: TFM file is bad")
-else print(" not usable: TFM file not found");
-help3("I wasn't able to read the size data for this font so this")@/
- ("`infont' operation won't produce anything. If the font name")@/
- ("is right, you might ask an expert to make a TFM file");
-if file_opened then
- help_line[0]:="is right, try asking an expert to fix the TFM file";
-error
-
-@ @<Read data from |tfm_infile|; if there is no room, say so...@>=
-@<Read the \.{TFM} size fields@>;
-@<Use the size fields to allocate space in |font_info|@>;
-@<Read the \.{TFM} header@>;
-@<Read the character data and the width, height, and depth tables and
- |goto done|@>
-
-@ A bad \.{TFM} file can be shorter than it claims to be. The code given here
-might try to read past the end of the file if this happens. Changes will be
-needed if it causes a system error to refer to |tfm_infile^| or call
-|get_tfm_infile| when |eof(tfm_infile)| is true. For example, the definition
-@^system dependencies@>
-of |tfget| could be changed to
-``|begin get(tfm_infile); if eof(tfm_infile) then goto bad_tfm; end|.''
-
-@d tfget==get(tfm_infile)
-@d tfbyte==tfm_infile^
-@d read_two(#)==begin #:=tfbyte;
- if #>127 then goto bad_tfm;
- tfget; #:=#*@'400+tfbyte;
- end
-@d tf_ignore(#)==for jj:=# downto 1 do tfget
-
-@<Read the \.{TFM} size fields@>=
-read_two(lf);
-tfget; read_two(lh);
-tfget; read_two(bc);
-tfget; read_two(ec);
-if (bc>1+ec)or(ec>255) then goto bad_tfm;
-tfget; read_two(nw);
-tfget; read_two(nh);
-tfget; read_two(nd);
-whd_size:=(ec+1-bc)+nw+nh+nd;
-if lf<6+lh+whd_size then goto bad_tfm;
-tf_ignore(10)
-
-@ Offsets are added to |char_base[n]| and |width_base[n]| so that is not
-necessary to apply the |so| and |qo| macros when looking up the width of a
-character in the string pool. In order to ensure nonnegative |char_base|
-values when |bc>0|, it may be necessary to reserve a few unused |font_info|
-elements.
-
-@<Use the size fields to allocate space in |font_info|@>=
-if next_fmem<bc+min_pool_ASCII then next_fmem:=bc+min_pool_ASCII;
- {ensure nonnegative |char_base|}
-if (last_fnum=font_max)or(next_fmem+whd_size>=font_mem_size) then
- @<Explain that there isn't enough space and |goto done|@>;
-incr(last_fnum);
-n:=last_fnum;
-font_bc[n]:=bc;
-font_ec[n]:=ec;
-char_base[n]:=next_fmem-bc-min_pool_ASCII;
-width_base[n]:=next_fmem+ec-bc+1-min_quarterword;
-height_base[n]:=width_base[n]+min_quarterword+nw;
-depth_base[n]:=height_base[n]+nh;
-next_fmem:=next_fmem+whd_size;
-
-@ @<Explain that there isn't enough space and |goto done|@>=
-begin print_err("Font ");
-print(fname);
-print(" not usable: Not enough space");
-help3("This `infont' operation won't produce anything because I")@/
- ("don't have enough room to store the character-size data for")@/
- ("the font. You may have to ask a wizard to enlarge me.");
-error;
-goto done;
-end
-
-@ @<Read the \.{TFM} header@>=
-if lh<2 then goto bad_tfm;
-tf_ignore(4);
-tfget; read_two(z);
-tfget; z:=z*@'400+tfbyte;
-tfget; z:=z*@'400+tfbyte; {now |z| is 16 times the design size}
-font_dsize[n]:=take_fraction(z,267432584);
- {times ${72\over72.27}2^{28}$ to convert from \TeX\ points}
-tf_ignore(4*(lh-2))
-
-@ @<Read the character data and the width, height, and depth tables...@>=
-ii:=width_base[n]+min_quarterword;
-i:=char_base[n]+min_pool_ASCII+bc;
-while i<ii do
- begin tfget; font_info[i].qqqq.b0:=qi(tfbyte);@/
- tfget; h_and_d:=tfbyte;
- font_info[i].qqqq.b1:=h_and_d div 16;
- font_info[i].qqqq.b2:=h_and_d mod 16;@/
- tfget; tfget;
- incr(i);
- end;
-while i<next_fmem do
- @<Read a four byte dimension, scale it by the design size, store it in
- |font_info[i]|, and increment |i|@>;
-if eof(tfm_infile) then goto bad_tfm;
-goto done
-
-@ The raw dimension read into |d| should have magnitude at most $2^{24}$ when
-interpreted as an integer, and this includes a scale factor of $2^{20}$. Thus
-we can multiply it by sixteen and think of it as a |fraction| that has been
-divided by sixteen. This cancels the extra scale factor contained in
-|font_dsize[n|.
-
-@<Read a four byte dimension, scale it by the design size, store it in...@>=
-begin tfget; d:=tfbyte;
-if d>=@'200 then d:=d-@'400;
-tfget; d:=d*@'400+tfbyte;@/
-tfget; d:=d*@'400+tfbyte;@/
-tfget; d:=d*@'400+tfbyte;@/
-font_info[i].sc:=take_fraction(d*16,font_dsize[n]);
-incr(i);
-end
-
-@ @<Open |tfm_infile| for input@>=
-file_opened:=false;
-str_scan_file(fname);
-if cur_area="" then cur_area:=MP_font_area;
-if cur_ext="" then cur_ext:=".tfm";
-pack_cur_name;
-if not b_open_in(tfm_infile) then goto bad_tfm;
-file_opened:=true
-
-@ When we have a font name and we don't know whether it has been loaded yet,
-we scan the |font_name| array before calling |read_font_info|.
-
-@<Declare text measuring subroutines@>=
-function find_font(@!f:str_number):font_number;
-label exit,found;
-var @!n:font_number;
-begin for n:=0 to last_fnum do
- if str_vs_str(f,font_name[n])=0 then goto found;
-find_font:=read_font_info(f);
-return;
-found:find_font:=n;
-exit:end;
-
-@ One simple application of |find_font| is the implementation of the |font_size|
-operator that gets the design size for a given font name.
-
-@<Find the design size of the font whose name is |cur_exp|@>=
-flush_cur_exp((font_dsize[find_font(cur_exp)]+8) div 16)
-
-@ If we discover that the font doesn't have a requested character, we omit it
-from the bounding box computation and expect the \ps\ interpreter to drop it.
-This routine issues a warning message if the user has asked for it.
-
-@<Declare text measuring subroutines@>=
-procedure lost_warning(@!f:font_number;@!k:pool_pointer);
-begin if internal[tracing_lost_chars]>0 then
- begin begin_diagnostic;
- if selector=log_only then incr(selector);
- print_nl("Missing character: There is no ");
-@.Missing character@>
- print(so(str_pool[k])); print(" in font ");
- print(font_name[f]); print_char("!"); end_diagnostic(false);
- end;
-end;
-
-@ The whole purpose of saving the height, width, and depth information is to be
-able to find the bounding box of an item of text in an edge structure. The
-|set_text_box| procedure takes a text node and adds this information.
-
-@<Declare text measuring subroutines@>=
-procedure set_text_box(@!p:pointer);
-var @!f:font_number; {|font_n(p)|}
-@!bc,@!ec:pool_ASCII_code; {range of valid characters for font |f|}
-@!k,kk:pool_pointer; {current character and character to stop at}
-@!cc:four_quarters; {the |char_info| for the current character}
-@!h,@!d:scaled; {dimensions of the current character}
-begin width_val(p):=0;
-height_val(p):=-el_gordo;
-depth_val(p):=-el_gordo;@/
-f:=font_n(p);
-bc:=si(font_bc[f]);
-ec:=si(font_ec[f]);@/
-kk:=str_stop(text_p(p));
-k:=str_start[text_p(p)];
-while k<kk do
- @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>;
-@<Set the height and depth to zero if the bounding box is empty@>;
-end;
-
-@ @<Adjust |p|'s bounding box to contain |str_pool[k]|; advance |k|@>=
-begin if (str_pool[k]<bc)or(str_pool[k]>ec) then lost_warning(f,k)
-else begin cc:=char_info(f)(str_pool[k]);
- if not ichar_exists(cc) then lost_warning(f,k)
- else begin width_val(p):=width_val(p)+char_width(f)(cc);
- h:=char_height(f)(cc);
- d:=char_depth(f)(cc);
- if h>height_val(p) then height_val(p):=h;
- if d>depth_val(p) then depth_val(p):=d;
- end;
- end;
-incr(k);
-end
-
-@ Let's hope modern compilers do comparisons correctly when the difference would
-overflow.
-
-@<Set the height and depth to zero if the bounding box is empty@>=
-if height_val(p)<-depth_val(p) then
- begin height_val(p):=0;
- depth_val(p):=0;
- end
-
-@ The new primitives fontmapfile and fontmapline.
-
-@<Declare action procedures for use by |do_statement|@>=
-procedure do_mapfile;
-begin get_x_next; scan_expression;
-if cur_type<>string_type then @<Complain about improper map operation@>
-else mpmapfile(cur_exp);
-end;
-procedure do_mapline;
-begin get_x_next; scan_expression;
-if cur_type<>string_type then @<Complain about improper map operation@>
-else begin mpmapline(cur_exp);
- end;
-end;
-
-@ @<Complain about improper map operation@>=
-begin exp_err("Unsuitable expression");
-help1("Only known strings can be map files or map lines.");
-put_get_error;
-end
-
-@ the C code needs to know how to get at the null font, how remove
-a string from memory, etc.
-
-@d is_valid_char(#)==((font_bc[f] <= #) and (# <= font_ec[f]) and
- ichar_exists(char_info(f)(#)))
-
-@p function get_nullfont: font_number;
-begin
- get_nullfont := null_font;
-end;
-@#
-procedure flush_str(s: str_number); {flush a string if possible}
-begin
- flush_string(s);
-end;
-@#
-function get_nullstr: str_number;
-begin
- get_nullstr := "";
-end;
-@#
-function get_termandlogid: integer;
-begin
- get_termandlogid:=term_and_log;
-end;
-@#
-function get_charwidth(f: font_number; c: eight_bits): scaled;
-begin
- if is_valid_char(c) then
- get_charwidth := char_width(f)(char_info(f)(c))
- else
- get_charwidth := 0;
-end;
-@#
-function tfm_lookup(s: str_number; fs: scaled): font_number;
-{looks up for a TFM with name |s| loaded at |fs| size; if found then flushes |s|}
-var k: font_number;
-begin
- if fs <> 0 then begin { should not be used! }
- for k := null_font + 1 to last_fnum do
- if str_vs_str(font_name[k], s) and (font_sizes[k] = fs) then begin
- flush_str(s);
- tfm_lookup := k;
- return;
- end;
- end
- else begin
- for k := null_font + 1 to last_fnum do
- if str_vs_str(font_name[k], s) then begin
- flush_str(s);
- tfm_lookup := k;
- return;
- end;
- end;
- tfm_lookup := null_font;
- exit:
-end;
-@#
-function new_dummy_font: font_number;
-begin
- new_dummy_font := read_font_info("dummy");
-end;
-@#
-function round_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);
-v := v mod d;
-if 2*v >= d then
- incr(u);
-if positive then
- round_xn_over_d := u
-else
- round_xn_over_d := -u;
-end;
-
-
-@
-@<Declare the \ps\ output procedures@>=
-procedure ps_print_cmd(@!l:str_number;@!s:str_number);
-begin
-if internal[mpprocset]>0 then begin ps_room(length(s)); print(s); end
-else begin ps_room(length(l)); print(l); end;
-end;
-@#
-procedure print_cmd(@!l:str_number;@!s:str_number);
-begin
-if internal[mpprocset]>0 then print(s) else print(l);
-end;
-@#
-function mp_char_marked(@!f:font_number;@!c: eight_bits): boolean;
-var @!b:integer; {|char_base[f]|}
-begin b:=char_base[f];
- if (c>=font_bc[f])and(c<=font_ec[f])and(font_info[b+c].qqqq.b3<>0) then
- mp_char_marked:=true
- else
- mp_char_marked:=false;
-end;
-
-@ The fontmap entries need a typedef
-
-@<Types...@>=
-fm_entry_ptr = ^integer;
-@!nonnegative_integer=0..@'17777777777; {$0\L x<2^{31}$}
-
-@ To print |scaled| value to PDF output we need some subroutines to ensure
-accurary.
-
-@d max_integer == @"7FFFFFFF {$2^{31}-1$}
-@d call_func(#) == begin if # <> 0 then do_nothing end
-
-@<Glob...@>=
-@!one_bp: scaled; {scaled value corresponds to 1bp}
-@!one_hundred_bp: scaled; {scaled value corresponds to 100bp}
-@!one_hundred_inch: scaled; {scaled value corresponds to 100in}
-@!ten_pow: array[0..9] of integer; {$10^0..10^9$}
-@!scaled_out: integer; {amount of |scaled| that was taken out in
-|divide_scaled|}
-
-@ @<Set init...@>=
-one_bp := 65782; {65781.76}
-one_hundred_bp := 6578176;
-one_hundred_inch := 473628672;
-ten_pow[0] := 1;
-for i := 1 to 9 do
- ten_pow[i] := 10*ten_pow[i - 1];
-mp_font_map:=xmalloc_array(fm_entry_ptr,font_max);
-for i := null_font to font_max do begin
- font_enc_name[i] := 0;
- font_ps_name_fixed[i] := false;
- mp_font_map[i] := 0;
- end;
-
-
-
-@ The following function divides |s| by |m|. |dd| is number of decimal digits.
-
-@p function divide_scaled(s, m: scaled; dd: integer): scaled;
-var q, r: scaled;
- sign, i: integer;
-begin
- sign := 1;
- if s < 0 then begin
- sign := -sign;
- s := -s;
- end;
- if m < 0 then begin
- sign := -sign;
- m := -m;
- end;
- if m = 0 then
- confusion("arithmetic: divided by zero")
- else if m >= (max_integer div 10) then
- confusion("arithmetic: number too big");
- q := s div m;
- r := s mod m;
- for i := 1 to dd do begin
- q := 10*q + (10*r) div m;
- r := (10*r) mod m;
- end;
- if 2*r >= m then begin
- incr(q);
- r := r - m;
- end;
- scaled_out := sign*(s - (r div ten_pow[dd]));
- divide_scaled := sign*q;
-end;
-
-@ The file |ps_tab_file| gives a table of \TeX\ font names and corresponding
-PostScript names for fonts that do not have to be downloaded, i.e., fonts that
-can be used when |internal[prologues]>0|. Each line consists of a \TeX\ name,
-one or more spaces, a PostScript name, and possibly a space and some other junk.
-This routine reads the table, updates |font_ps_name| entries starting after
-|last_ps_fnum|, and sets |last_ps_fnum:=last_fnum|. If the file |ps_tab_file|
-is missing, we assume that the existing font names are OK and nothing needs to
-be done.
-
-@<Declare the \ps\ output procedures@>=
-procedure read_psname_table;
-label common_ending, done;
-var @!k:font_number; {font for possible name match}
-@!lmax:integer; {upper limit on length of name to match}
-@!j:integer; {characters left to read before string gets too long}
-@!c:text_char; {character being read from |ps_tab_file|}
-@!s:str_number; {possible font name to match}
-begin name_of_file:=ps_tab_name;
-if a_open_in(ps_tab_file) then
- begin @<Set |lmax| to the maximum |font_name| length for fonts
- |last_ps_fnum+1| through |last_fnum|@>;
- while not eof(ps_tab_file) do
- begin @<Read at most |lmax| characters from |ps_tab_file| into string |s|
- but |goto common_ending| if there is trouble@>;
- for k:=last_ps_fnum+1 to last_fnum do
- if str_vs_str(s,font_name[k])=0 then
- @<|flush_string(s)|, read in |font_ps_name[k]|, and
- |goto common_ending|@>;
- flush_string(s);
-common_ending:read_ln(ps_tab_file);
- end;
- last_ps_fnum:=last_fnum;
- a_close(ps_tab_file);
- end;
-end;
-
-@ @<Glob...@>=
-@!ps_tab_file:alpha_file; {file for font name translation table}
-
-@ @<Set |lmax| to the maximum |font_name| length for fonts...@>=
-lmax:=0;
-for k:=last_ps_fnum+1 to last_fnum do
- if length(font_name[k])>lmax then lmax:=length(font_name[k])
-
-@ @<Read at most |lmax| characters from |ps_tab_file| into string |s|...@>=
-str_room(lmax);
-j:=lmax;
-loop @+begin if eoln(ps_tab_file) then
- fatal_error("The psfont map file is bad!");
- read(ps_tab_file,c);
- if c=' ' then goto done;
- decr(j);
- if j>=0 then append_char(xord[c])
- else begin flush_cur_string;
- goto common_ending;
- end;
- end;
-done:s:=make_string
-
-@ PostScript font names should be at most 28 characters long but we allow 32
-just to be safe.
-
-@<|flush_string(s)|, read in |font_ps_name[k]|, and...@>=
-begin flush_string(s);
-j:=32;
-str_room(j);
-repeat if eoln(ps_tab_file) then fatal_error("The psfont map file is bad!");
- read(ps_tab_file,c);
-until c<>' ';
-repeat decr(j);
- if j<0 then fatal_error("The psfont map file is bad!");
- append_char(xord[c]);
- if eoln(ps_tab_file) then c:=' ' @+else read(ps_tab_file,c);
-until c=' ';
-delete_str_ref(font_ps_name[k]);
-font_ps_name[k]:=make_string;
-goto common_ending;
-end
-
-@* \[44] Shipping pictures out.
-The |ship_out| procedure, to be described below, is given a pointer to
-an edge structure. Its mission is to output a file containing the \ps\
-description of an edge structure.
-
-@ Each time an edge structure is shipped out we write a new \ps\ output
-file named according to the current \&{charcode}.
-@:char_code_}{\&{charcode} primitive@>
-
-@<Declare the \ps\ output procedures@>=
-procedure open_output_file;
-label @!continue; { after a digit is seen}
-var @!c:integer; {\&{charcode} rounded to the nearest integer}
-@!old_setting:0..max_selector; {previous |selector| setting}
-@!s,n:str_number; {a file extension derived from |c|}
-@!i:pool_pointer; { indexes into |filename_template| }
-@!cc:integer; {a temporary integer for template building }
-@!f,g:integer; {field widths}
-begin if job_name=0 then open_log_file;
-c:=round_unscaled(internal[char_code]);
-if filename_template=0 then begin
- if c<0 then s:=".ps"
- else @<Use |c| to compute the file extension |s|@>;
- pack_job_name(s);
- while not a_open_out(ps_file) do
- prompt_file_name("file name for output",s);
- end
-else begin
- {initializations}
- s := "";
- n := "";
- old_setting:=selector; selector:=new_string;
- f := 0;
- i := str_start[filename_template];
- while i<str_stop(filename_template) do begin
- if so(str_pool[i])="%" then begin
- continue:
- incr(i);
- if i<str_stop(filename_template) then
- begin
- if so(str_pool[i])="j" then
- print(job_name)
- else if so(str_pool[i])="d" then begin
- cc:= round_unscaled(internal[day]);
- print_with_leading_zeroes(cc);
- end
- else if so(str_pool[i])="m" then begin
- cc:= round_unscaled(internal[month]);
- print_with_leading_zeroes(cc);
- end
- else if so(str_pool[i])="y" then begin
- cc:= round_unscaled(internal[year]);
- print_with_leading_zeroes(cc);
- end
- else if so(str_pool[i])="H" then begin
- cc:= round_unscaled(internal[time]) div 60;
- print_with_leading_zeroes(cc);
- end
- else if so(str_pool[i])="M" then begin
- cc:= round_unscaled(internal[time]) mod 60;
- print_with_leading_zeroes(cc);
- end
- else if so(str_pool[i])="c" then begin
- if c<0 then print("ps")
- else begin
- print_with_leading_zeroes(c);
- end;
- end
- else if (so(str_pool[i])>="0") and (so(str_pool[i])<="9") then begin
- if (f<10) then
- f := (f*10) + so(str_pool[i])-"0";
- goto continue;
- end
- else
- print(str_pool[i])
- end
- end
- else begin
- if so(str_pool[i])="." then
- if n="" then
- n := make_string;
- print(str_pool[i]);
- end;
- incr(i);
- end;
- s := make_string;
- selector:= old_setting;
- if n="" then begin
- n:=s;
- s:="";
- end;
- pack_file_name(n,"",s);
- while not a_open_out(ps_file) do
- prompt_file_name("file name for output",s);
- delete_str_ref(n);
- end;
-delete_str_ref(s);
-@<Store the true output file name if appropriate@>;
-@<Begin the progress report for the ouput of picture~|c|@>;
-end;
-
-@ The file extension created here could be up to five characters long in
-extreme cases so it may have to be shortened on some systems.
-@^system dependencies@>
-
-@<Use |c| to compute the file extension |s|@>=
-begin old_setting:=selector; selector:=new_string;
-print_char("."); print_int(c);
-s:=make_string;
-selector:=old_setting;
-end
-
-@ The user won't want to see all the output file names so we only save the
-first and last ones and a count of how many there were. For this purpose
-files are ordered primarily by \&{charcode} and secondarily by order of
-creation.
-@:char_code_}{\&{charcode} primitive@>
-
-@<Store the true output file name if appropriate@>=
-if (c<first_output_code)and(first_output_code>=0) then
- begin first_output_code:=c;
- delete_str_ref(first_file_name);
- first_file_name:=a_make_name_string(ps_file);
- end;
-if c>=last_output_code then
- begin last_output_code:=c;
- delete_str_ref(last_file_name);
- last_file_name:=a_make_name_string(ps_file);
- end
-
-@ @<Glob...@>=
-@!first_file_name,@!last_file_name:str_number; {full file names}
-@!first_output_code,@!last_output_code:integer; {rounded \&{charcode} values}
-@:char_code_}{\&{charcode} primitive@>
-@!total_shipped:integer; {total number of |ship_out| operations completed}
-
-@ @<Set init...@>=
-first_file_name:="";
-last_file_name:="";@/
-first_output_code:=32768;
-last_output_code:=-32768;@/
-total_shipped:=0;
-
-@ @<Begin the progress report for the ouput of picture~|c|@>=
-if term_offset>max_print_line-6 then print_ln
-else if (term_offset>0)or(file_offset>0) then print_char(" ");
-print_char("[");
-if c>=0 then print_int(c)
-
-@ @<End progress report@>=
-print_char("]");
-update_terminal;
-incr(total_shipped)
-
-@ @<Explain what output files were written@>=
-if total_shipped>0 then
- begin print_nl("");
- print_int(total_shipped);
- print(" output file");
- if total_shipped>1 then print_char("s");
- print(" written: ");
- print(first_file_name);
- if total_shipped>1 then
- begin if 31+length(first_file_name)+length(last_file_name)>@|
- max_print_line
- then print_ln;
- print(" .. ");
- print(last_file_name);
- end;
- end
-
-@ We often need to print a pair of coordinates.
-
-@d ps_room(#)==if ps_offset+#>max_print_line then print_ln {optional line break}
-
-@<Declare the \ps\ output procedures@>=
-procedure ps_pair_out(@!x,@!y:scaled);
-begin ps_room(26);
-print_scaled(x); print_char(" ");
-print_scaled(y); print_char(" ")
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-procedure ps_print(@!s:str_number);
-begin ps_room(length(s));
-print(s);
-end;
-
-@ The most important output procedure is the one that gives the \ps\ version of
-a \MP\ path.
-
-@<Declare the \ps\ output procedures@>=
-procedure ps_path_out(@!h:pointer);
-label exit;
-var @!p,@!q:pointer; {for scanning the path}
-@!d:scaled; {a temporary value}
-@!curved:boolean; {|true| unless the cubic is almost straight}
-begin ps_room(40);
-if need_newpath then print_cmd("newpath ","n ");
-need_newpath:=true;
-ps_pair_out(x_coord(h),y_coord(h));
-print_cmd("moveto","m");@/
-p:=h;
-repeat if right_type(p)=endpoint then
- begin if p=h then ps_print_cmd(" 0 0 rlineto"," 0 0 r");
- return;
- end;
-q:=link(p);
-@<Start a new line and print the \ps\ commands for the curve from
- |p| to~|q|@>;
-p:=q;
-until p=h;
-ps_print_cmd(" closepath"," p");
-exit:end;
-
-@ @<Glob...@>=
-need_newpath:boolean;
- {will |ps_path_out| need to issue a \&{newpath} command next time}
-@:newpath_}{\&{newpath} command@>
-
-@ @<Start a new line and print the \ps\ commands for the curve from...@>=
-curved:=true;
-@<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>;
-print_ln;
-if curved then
- begin ps_pair_out(right_x(p),right_y(p));
- ps_pair_out(left_x(q),left_y(q));
- ps_pair_out(x_coord(q),y_coord(q));
- ps_print_cmd("curveto","c");
- end
-else if q<>h then
- begin ps_pair_out(x_coord(q),y_coord(q));
- ps_print_cmd("lineto","l");
- end
-
-@ Two types of straight lines come up often in \MP\ paths:
-cubics with zero initial and final velocity as created by |make_path| or
-|make_envelope|, and cubics with control points uniformly spaced on a line
-as created by |make_choices|.
-
-@d bend_tolerance=131 {allow rounding error of $2\cdot10^{-3}$}
-
-@<Set |curved:=false| if the cubic from |p| to |q| is almost straight@>=
-if right_x(p)=x_coord(p) then
- if right_y(p)=y_coord(p) then
- if left_x(q)=x_coord(q) then
- if left_y(q)=y_coord(q) then curved:=false;
-d:=left_x(q)-right_x(p);
-if abs(right_x(p)-x_coord(p)-d)<=bend_tolerance then
- if abs(x_coord(q)-left_x(q)-d)<=bend_tolerance then
- begin d:=left_y(q)-right_y(p);
- if abs(right_y(p)-y_coord(p)-d)<=bend_tolerance then
- if abs(y_coord(q)-left_y(q)-d)<=bend_tolerance then curved:=false;
- end
-
-@ We need to keep track of several parameters from the \ps\ graphics state.
-@^graphics state@>
-This allows us to be sure that \ps\ has the correct values when they are
-needed without wasting time and space setting them unnecessarily.
-
-@d gs_node_size=10
-@d gs_red ==mem[gs_state+1].sc
-@d gs_green ==mem[gs_state+2].sc
-@d gs_blue ==mem[gs_state+3].sc
-@d gs_black ==mem[gs_state+4].sc
- {color from the last \&{setcmykcolor} or \&{setrgbcolor} or \&{setgray} command}
-@d gs_colormodel==mem[gs_state+5].qqqq.b0
- {the current colormodel}
-@d gs_ljoin ==mem[gs_state+5].qqqq.b1
-@d gs_lcap ==mem[gs_state+5].qqqq.b2
- {values from the last \&{setlinejoin} and \&{setlinecap} commands}
-@d gs_adj_wx ==mem[gs_state+5].qqqq.b3
- {what resolution-dependent adjustment applies to the width}
-@d gs_miterlim ==mem[gs_state+6].sc
- {the value from the last \&{setmiterlimit} command}
-@d gs_dash_p ==mem[gs_state+7].hh.lh
- {edge structure for last \&{setdash} command}
-@d gs_previous ==mem[gs_state+7].hh.rh
- {backlink to the previous |gs_state| structure}
-@d gs_dash_sc ==mem[gs_state+8].sc
- {scale factor used with |gs_dash_p|}
-@d gs_width ==mem[gs_state+9].sc
- {width setting or $-1$ if no \&{setlinewidth} command so far}
-
-@<Glob...@>=
-gs_state:pointer;
-
-@ @<Set init...@>=
-gs_state:=null;
-
-@ To avoid making undue assumptions about the initial graphics state, these
-parameters are given special values that are guaranteed not to match anything
-in the edge structure being shipped out. On the other hand, the initial color
-should be black so that the translation of an all-black picture will have no
-\&{setcolor} commands. (These would be undesirable in a font application.)
-Hence we use |c=0| when initializing the graphics state and we use |c<0|
-to recover from a situation where we have lost track of the graphics state.
-
-@<Declare the \ps\ output procedures@>=
-procedure unknown_graphics_state(c:scaled);
-var @!p:pointer; {to shift graphic states around}
- @!k:quarterword; {a loop index for copying the |gs_state|}
-begin if (c=0)or(c=-1) then begin
- if gs_state=null then begin
- gs_state := get_node(gs_node_size);
- gs_previous:=null;
- end
- else begin
- while gs_previous<>null do begin
- p := gs_previous;
- free_node(gs_state,gs_node_size);
- gs_state:=p;
- end;
- end;
- gs_red:=c; gs_green:=c; gs_blue:=c; gs_black:=c;@/
- gs_colormodel:=uninitialized_model;
- gs_ljoin:=3;
- gs_lcap:=3;
- gs_miterlim:=0;@/
- gs_dash_p:=void;
- gs_dash_sc:=0;
- gs_width:=-1;
- end
-else if c=1 then begin
- p:= gs_state;
- gs_state := get_node(gs_node_size);
- for k:=1 to gs_node_size-1 do
- mem[gs_state+k]:=mem[p+k];
- gs_previous := p;
- end
-else if c=2 then begin
- p := gs_previous;
- free_node(gs_state,gs_node_size);
- gs_state:=p;
- end;
-end;
-
-@ When it is time to output a graphical object, |fix_graphics_state| ensures
-that \ps's idea of the graphics state agrees with what is stored in the object.
-
-@<Declare the \ps\ output procedures@>=
-@<Declare subroutines needed by |fix_graphics_state|@>@;
-procedure fix_graphics_state(p:pointer);
- {get ready to output graphical object |p|}
-var @!hh,@!pp:pointer; {for list manipulation}
-@!wx,@!wy,@!ww:scaled; {dimensions of pen bounding box}
-@!adj_wx:boolean; {whether pixel rounding should be based on |wx| or |wy|}
-@!tx,@!ty:integer; {temporaries for computing |adj_wx|}
-@!scf:scaled; {a scale factor for the dash pattern}
-begin if has_color(p) then
- @<Make sure \ps\ will use the right color for object~|p|@>;
-if (type(p)=fill_code)or(type(p)=stroked_code) then
- if pen_p(p)<>null then
- if pen_is_elliptical(pen_p(p)) then
- begin @<Generate \ps\ code that sets the stroke width to the
- appropriate rounded value@>;
- @<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>;
- @<Decide whether the line cap parameter matters and set it if necessary@>;
- @<Set the other numeric parameters as needed for object~|p|@>;
- end;
-if ps_offset>0 then print_ln;
-end;
-
-@ @<Decide whether the line cap parameter matters and set it if necessary@>=
-if type(p)=stroked_code then
- if (left_type(path_p(p))=endpoint)or(dash_p(p)<>null) then
- if gs_lcap<>lcap_val(p) then
- begin ps_room(13);
- print_char(" ");
- print_char("0"+lcap_val(p)); print_cmd(" setlinecap"," lc");
- gs_lcap:=lcap_val(p);
- end
-
-@ @<Set the other numeric parameters as needed for object~|p|@>=
-if gs_ljoin<>ljoin_val(p) then
- begin ps_room(14);
- print_char(" ");
- print_char("0"+ljoin_val(p)); print_cmd(" setlinejoin"," lj");
- gs_ljoin:=ljoin_val(p);
- end;
-if gs_miterlim<>miterlim_val(p) then
- begin ps_room(27);
- print_char(" ");
- print_scaled(miterlim_val(p)); print_cmd(" setmiterlimit"," ml");
- gs_miterlim:=miterlim_val(p);
- end
-
-@ @<Make sure \ps\ will use the right color for object~|p|@>=
-begin
- if (color_model(p)=rgb_model)or@|
- ((color_model(p)=uninitialized_model)and
- ((internal[default_color_model] div unity)=rgb_model)) then
- begin if (gs_colormodel<>rgb_model)or(gs_red<>red_val(p))or@|
- (gs_green<>green_val(p))or(gs_blue<>blue_val(p)) then
- begin gs_red:=red_val(p);
- gs_green:=green_val(p);
- gs_blue:=blue_val(p);
- gs_black:= -1;@/
- gs_colormodel:=rgb_model;
- begin ps_room(36);
- print_char(" ");
- print_scaled(gs_red); print_char(" ");
- print_scaled(gs_green); print_char(" ");
- print_scaled(gs_blue);
- print_cmd(" setrgbcolor", " R");
- end;
- end;
- end
- else if (color_model(p)=cmyk_model)or@|
- ((color_model(p)=uninitialized_model)and
- ((internal[default_color_model] div unity)=cmyk_model)) then
- begin if (gs_red<>cyan_val(p))or(gs_green<>magenta_val(p))or@|
- (gs_blue<>yellow_val(p))or(gs_black<>black_val(p))or@|
- (gs_colormodel<>cmyk_model) then
- begin
- if color_model(p)=uninitialized_model then begin
- gs_red:=0;
- gs_green:=0;
- gs_blue:=0;
- gs_black:=unity;@/
- end
- else begin
- gs_red:=cyan_val(p);
- gs_green:=magenta_val(p);
- gs_blue:=yellow_val(p);
- gs_black:=black_val(p);@/
- end;
- gs_colormodel:=cmyk_model;
- begin ps_room(45);
- print_char(" ");
- print_scaled(gs_red); print_char(" ");
- print_scaled(gs_green); print_char(" ");
- print_scaled(gs_blue); print_char(" ");
- print_scaled(gs_black);
- print_cmd(" setcmykcolor"," C");
- end;
- end;
- end
- else if (color_model(p)=grey_model)or@|
- ((color_model(p)=uninitialized_model)and
- ((internal[default_color_model] div unity)=grey_model)) then
- begin if (gs_red<>grey_val(p))or(gs_colormodel<>grey_model) then
- begin gs_red := grey_val(p);
- gs_green:= -1;
- gs_blue:= -1;
- gs_black:= -1;@/
- gs_colormodel:=grey_model;
- begin ps_room(16);
- print_char(" ");
- print_scaled(gs_red);
- print_cmd(" setgray"," G");
- end;
- end;
- end;
- if color_model(p)=no_model then
- gs_colormodel:=no_model;
-end
-
-@ In order to get consistent widths for horizontal and vertical pen strokes, we
-want \ps\ to use an integer number of pixels for the \&{setwidth} parameter.
-@:setwidth}{\&{setwidth}command@>
-We set |gs_width| to the ideal horizontal or vertical stroke width and then
-generate \ps\ code that computes the rounded value. For non-circular pens, the
-pen shape will be rescaled so that horizontal or vertical parts of the stroke
-have the computed width.
-
-Rounding the width to whole pixels is not likely to improve the appearance of
-diagonal or curved strokes, but we do it anyway for consistency. The
-\&{truncate} command generated here tends to make all the strokes a little
-@:truncate}{\&{truncate} command@>
-thinner, but this is appropriate for \ps's scan-conversion rules. Even with
-truncation, an ideal with of $w$~pixels gets mapped into $\lfloor w\rfloor+1$.
-It would be better to have $\lceil w\rceil$ but that is ridiculously expensive
-to compute in \ps.
-
-@<Generate \ps\ code that sets the stroke width...@>=
-@<Set |wx| and |wy| to the width and height of the bounding box for
- |pen_p(p)|@>;
-@<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more
- important and set |adj_wx| and |ww| accordingly@>;
-if (ww<>gs_width) or (adj_wx<>gs_adj_wx) then
- begin if adj_wx then
- begin ps_room(13);
- print_char(" "); print_scaled(ww);
- ps_print_cmd(" 0 dtransform exch truncate exch idtransform pop setlinewidth"," hlw");
- end
- else begin
- if internal[mpprocset]>0 then begin
- ps_room(13);
- print_char(" ");
- print_scaled(ww);
- ps_print(" vlw");
- end
- else begin ps_room(15);
- print(" 0 "); print_scaled(ww);
- ps_print(" dtransform truncate idtransform setlinewidth pop");
- end;
- end;
- gs_width := ww;
- gs_adj_wx := adj_wx;
- end
-
-@ @<Set |wx| and |wy| to the width and height of the bounding box for...@>=
-pp:=pen_p(p);
-if (right_x(pp)=x_coord(pp)) and (left_y(pp)=y_coord(pp)) then
- begin wx := abs(left_x(pp) - x_coord(pp));
- wy := abs(right_y(pp) - y_coord(pp));
- end
-else begin
- wx := pyth_add(left_x(pp)-x_coord(pp), right_x(pp)-x_coord(pp));
- wy := pyth_add(left_y(pp)-y_coord(pp), right_y(pp)-y_coord(pp));
- end
-
-@ The path is considered ``essentially horizontal'' if its range of
-$y$~coordinates is less than the $y$~range |wy| for the pen. ``Essentially
-vertical'' paths are detected similarly. This code ensures that no component
-of the pen transformation is more that |aspect_bound*(ww+1)|.
-
-@d aspect_bound=10 {``less important'' of |wx|, |wy| cannot exceed the other by
- more than this factor}
-
-@<Use |pen_p(p)| and |path_p(p)| to decide whether |wx| or |wy| is more...@>=
-tx:=1; ty:=1;
-if coord_rangeOK(path_p(p), y_loc(0), wy) then tx:=aspect_bound
-else if coord_rangeOK(path_p(p), x_loc(0), wx) then ty:=aspect_bound;
-if wy div ty>=wx div tx then
- begin ww:=wy; adj_wx:=false;
- end
-else begin ww:=wx; adj_wx:=true;
- end
-
-@ This routine quickly tests if path |h| is ``essentially horizontal'' or
-``essentially vertical,'' where |zoff| is |x_loc(0)| or |y_loc(0)| and |dz| is
-allowable range for $x$ or~$y$. We do not need and cannot afford a full
-bounding-box computation.
-
-@<Declare subroutines needed by |fix_graphics_state|@>=
-function coord_rangeOK(@!h:pointer; @!zoff:small_number; dz:scaled):boolean;
-label found, not_found, exit;
-var @!p:pointer; {for scanning the path form |h|}
- @!zlo,@!zhi:scaled; {coordinate range so far}
- @!z:scaled; {coordinate currently being tested}
-begin zlo:=knot_coord(h+zoff);
-zhi:=zlo;
-p:=h;
-while right_type(p)<>endpoint do
- begin z:=right_coord(p+zoff);@/
- @<Make |zlo..zhi| include |z| and |goto found| if |zhi-zlo>dz|@>;
- p:=link(p);
- z:=left_coord(p+zoff);@/
- @<Make |zlo..zhi| include |z| and |goto found| if |zhi-zlo>dz|@>;
- z:=knot_coord(p+zoff);@/
- @<Make |zlo..zhi| include |z| and |goto found| if |zhi-zlo>dz|@>;
- if p=h then goto not_found;
- end;
-not_found:coord_rangeOK:=true;
-return;
-found:coord_rangeOK:=false;
-exit:end;
-
-@ @<Make |zlo..zhi| include |z| and |goto found| if |zhi-zlo>dz|@>=
-if z<zlo then zlo:=z
-else if z>zhi then zhi:=z;
-if zhi-zlo>dz then goto found
-
-@ Filling with an elliptical pen is implemented via a combination of \&{stroke}
-and \&{fill} commands and a nontrivial dash pattern would interfere with this.
-@:stroke}{\&{stroke} command@>
-@:fill}{\&{fill} command@>
-Note that we don't use |delete_edge_ref| because |gs_dash_p| is not counted as
-a reference.
-
-@<Make sure \ps\ will use the right dash pattern for |dash_p(p)|@>=
-if type(p)=fill_code then hh:=null
-else begin hh:=dash_p(p);
- scf:=get_pen_scale(pen_p(p));
- if scf=0 then
- if gs_width=0 then scf:=dash_scale(p) @+else hh:=null
- else begin scf:=make_scaled(gs_width,scf);
- scf:=take_scaled(scf,dash_scale(p));
- end;
- end;
-if hh=null then
- begin if gs_dash_p<>null then
- begin ps_print_cmd(" [] 0 setdash"," rd");
- gs_dash_p:=null;
- end;
- end
-else if (gs_dash_sc<>scf) or not same_dashes(gs_dash_p,hh) then
- @<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>
-
-@ Translating a dash list into \ps\ is very similar to printing it symbolically
-in |print_edges|. A dash pattern with |dash_y(hh)=0| has length zero and is
-ignored. The same fate applies in the bizarre case of a dash pattern that
-cannot be printed without overflow.
-
-@<Set the dash pattern from |dash_list(hh)| scaled by |scf|@>=
-begin gs_dash_p:=hh;
-gs_dash_sc:=scf;
-if (dash_y(hh)=0) or (abs(dash_y(hh)) div unity >= el_gordo div scf) then
- ps_print_cmd(" [] 0 setdash"," rd")
-else begin pp:=dash_list(hh);
- start_x(null_dash):=start_x(pp)+dash_y(hh);@/
- ps_room(28);
- print(" [");
- while pp<>null_dash do
- begin ps_pair_out(take_scaled(stop_x(pp)-start_x(pp),scf),@|
- take_scaled(start_x(link(pp))-stop_x(pp),scf));
- pp:=link(pp);
- end;
- ps_room(22);
- print("] ");
- print_scaled(take_scaled(dash_offset(hh),scf));
- print_cmd(" setdash"," sd");
- end;
-end
-
-@ @<Declare subroutines needed by |fix_graphics_state|@>=
-function same_dashes(@!h,@!hh:pointer):boolean;
- {do |h| and |hh| represent the same dash pattern?}
-label done;
-var @!p,@!pp:pointer; {dash nodes being compared}
-begin if h=hh then same_dashes:=true
-else if (h<=void)or(hh<=void) then same_dashes:=false
-else if dash_y(h)<>dash_y(hh) then same_dashes:=false
-else @<Compare |dash_list(h)| and |dash_list(hh)|@>;
-end;
-
-@ @<Compare |dash_list(h)| and |dash_list(hh)|@>=
-begin p:=dash_list(h);
-pp:=dash_list(hh);
-while (p<>null_dash)and(pp<>null_dash) do
- if (start_x(p)<>start_x(pp))or(stop_x(p)<>stop_x(pp)) then goto done
- else begin p:=link(p);
- pp:=link(pp);
- end;
-done:same_dashes:=p=pp;
-end
-
-@ When stroking a path with an elliptical pen, it is necessary to transform
-the coordinate system so that a unit circular pen will have the desired shape.
-To keep this transformation local, we enclose it in a
-$$\&{gsave}\ldots\&{grestore}$$
-block. Any translation component must be applied to the path being stroked
-while the rest of the transformation must apply only to the pen.
-If |fill_also=true|, the path is to be filled as well as stroked so we must
-insert commands to do this after giving the path.
-
-@<Declare the \ps\ output procedures@>=
-procedure stroke_ellipse(@!h:pointer;@!fill_also:boolean);
- {generate an elliptical pen stroke from object |h|}
-var @!txx,@!txy,@!tyx,@!tyy:scaled; {transformation parameters}
-@!p:pointer; {the pen to stroke with}
-@!d1,@!det:scaled; {for tweaking transformation parameters}
-@!s:integer; {also for tweaking transformation paramters}
-@!transformed:boolean; {keeps track of whether gsave/grestore are needed}
-begin transformed:=false;@/
-@<Use |pen_p(h)| to set the transformation parameters and give the initial
- translation@>;
-@<Tweak the transformation parameters so the transformation is nonsingular@>;
-ps_path_out(path_p(h));@/
-if internal[mpprocset]=0 then begin
- if fill_also then print_nl("gsave fill grestore");
- @<Issue \ps\ commands to transform the coordinate system@>;
- ps_print(" stroke");
- if transformed then ps_print(" grestore");
- end
-else begin
- if fill_also then print_nl("B") else print_ln;
- if (txy<>0)or(tyx<>0) then
- begin
- print(" [");
- ps_pair_out(txx,tyx);
- ps_pair_out(txy,tyy);@/
- ps_print("0 0] t");
- end
- else if (txx<>unity)or(tyy<>unity) then
- begin
- ps_pair_out(txx,tyy);
- print(" s");
- end;
- ps_print(" S");
- if transformed then ps_print(" Q");
-end;
-print_ln;
-end;
-
-@ @<Use |pen_p(h)| to set the transformation parameters and give the...@>=
-p:=pen_p(h);
-txx:=left_x(p);
-tyx:=left_y(p);@/
-txy:=right_x(p);
-tyy:=right_y(p);
-if (x_coord(p)<>0)or(y_coord(p)<>0) then
- begin print_nl(""); print_cmd("gsave ","q ");
- ps_pair_out(x_coord(p),y_coord(p));
- ps_print("translate ");@/
- txx:=txx-x_coord(p);
- tyx:=tyx-y_coord(p);@/
- txy:=txy-x_coord(p);
- tyy:=tyy-y_coord(p);
- transformed:=true;
- end
-else print_nl("");
-@<Adjust the transformation to account for |gs_width| and output the
- initial \&{gsave} if |transformed| should be |true|@>
-
-@ @<Adjust the transformation to account for |gs_width| and output the...@>=
-if gs_width<>unity then
- if gs_width=0 then
- begin txx:=unity; tyy:=unity;
- end
- else begin txx:=make_scaled(txx,gs_width);
- txy:=make_scaled(txy,gs_width);
- tyx:=make_scaled(tyx,gs_width);
- tyy:=make_scaled(tyy,gs_width);
- end;
-if (txy<>0)or(tyx<>0)or(txx<>unity)or(tyy<>unity) then
- if (not transformed) then
- begin ps_print_cmd("gsave ","q ");
- transformed:=true;
- end
-
-@ @<Issue \ps\ commands to transform the coordinate system@>=
-if (txy<>0)or(tyx<>0) then
- begin print_ln;
- print_char("[");
- ps_pair_out(txx,tyx);
- ps_pair_out(txy,tyy);@/
- ps_print("0 0] concat");
- end
-else if (txx<>unity)or(tyy<>unity) then
- begin print_ln;
- ps_pair_out(txx,tyy);
- print("scale");
- end
-
-@ The \ps\ interpreter will probably abort if it encounters a singular
-transformation matrix. The determinant must be large enough to ensure that
-the printed representation will be nonsingular. Since the printed
-representation is always within $2^{-17}$ of the internal |scaled| value, the
-total error is at most $4T_{\rm max}2^{-17}$, where $T_{\rm max}$ is a bound on
-the magnitudes of |txx/65536|, |txy/65536|, etc.
-
-The |aspect_bound*(gs_width+1)| bound on the components of the pen
-transformation allows $T_{\rm max}$ to be at most |2*aspect_bound|.
-
-@<Tweak the transformation parameters so the transformation is nonsingular@>=
-det:=take_scaled(txx,tyy) - take_scaled(txy,tyx);
-d1:=4*aspect_bound+1;
-if abs(det)<d1 then
- begin if det>=0 then
- begin d1:=d1-det; s:=1; @+end
- else begin d1:=-d1-det; s:=-1; @+end;
- d1:=d1*unity;
- if abs(txx)+abs(tyy)>=abs(txy)+abs(tyy) then
- if abs(txx)>abs(tyy) then tyy:=tyy+(d1+s*abs(txx)) div txx
- else txx:=txx+(d1+s*abs(tyy)) div tyy
- else if abs(txy)>abs(tyx) then tyx:=tyx+(d1+s*abs(txy)) div txy
- else txy:=txy+(d1+s*abs(tyx)) div tyx;
- end
-
-@ Here is a simple routine that just fills a cycle.
-
-@<Declare the \ps\ output procedures@>=
-procedure ps_fill_out(@!p:pointer); {fill cyclic path~|p|}
-begin ps_path_out(p);
-ps_print_cmd(" fill"," F");
-print_ln;
-end;
-
-@ Given a cyclic path~|p| and a graphical object~|h|, the |do_outer_envelope|
-procedure fills the cycle generated by |make_envelope|. It need not do
-anything unless some region has positive winding number with respect to~|p|,
-but it does not seem worthwhile to for test this.
-
-@<Declare the \ps\ output procedures@>=
-procedure do_outer_envelope(@!p,@!h:pointer);
-begin p:=make_envelope(p, pen_p(h), ljoin_val(h), 0, miterlim_val(h));
-ps_fill_out(p);
-toss_knot_list(p);
-end;
-
-@ A text node may specify an arbitrary transformation but the usual case
-involves only shifting, scaling, and occasionally rotation. The purpose
-of |choose_scale| is to select a scale factor so that the remaining
-transformation is as ``nice'' as possible. The definition of ``nice''
-is somewhat arbitrary but shifting and $90^\circ$ rotation are especially
-nice because they work out well for bitmap fonts. The code here selects
-a scale factor equal to $1/\sqrt2$ times the Frobenius norm of the
-non-shifting part of the transformation matrix. It is careful to avoid
-additions that might cause undetected overflow.
-
-@<Declare the \ps\ output procedures@>=
-function choose_scale(@!p:pointer):scaled; {|p| should point to a text node}
-var @!a,@!b,@!c,@!d,@!ad,@!bc:scaled; {temporary values}
-begin a:=txx_val(p);
-b:=txy_val(p);
-c:=tyx_val(p);
-d:=tyy_val(p);@/
-if (a<0) then negate(a);
-if (b<0) then negate(b);
-if (c<0) then negate(c);
-if (d<0) then negate(d);
-ad:=half(a-d);
-bc:=half(b-c);@/
-choose_scale:=pyth_add(pyth_add(d+ad,ad), pyth_add(c+bc,bc));
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-procedure ps_string_out(s:str_number);
-var @!i:pool_pointer; {current character code position}
-@!k:ASCII_code; {bits to be converted to octal}
-begin print("(");
-i:=str_start[s];
-while i<str_stop(s) do
- begin if ps_offset+5>max_print_line then
- begin print_char("\");
- print_ln;
- end;
- k:=so(str_pool[i]);
- if (@<Character |k| is not allowed in PostScript output@>) then
- begin print_char("\");
- print_char("0"+(k div 64));
- print_char("0"+((k div 8) mod 8));
- print_char("0"+(k mod 8));
- end
- else begin if (k="(")or(k=")")or(k="\") then print_char("\");
- print_char(k);
- end;
- incr(i);
- end;
-print(")");
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-function is_ps_name(@!s:str_number):boolean;
-label not_found,exit;
-var @!i:pool_pointer; {current character code position}
-@!k:ASCII_code; {the character being checked}
-begin i:=str_start[s];
-while i<str_stop(s) do
- begin k:=so(str_pool[i]);
- if (k<=" ")or(k>"~") then goto not_found;
- if (k="(")or(k=")")or(k="<")or(k=">")or@|
- (k="{")or(k="}")or(k="/")or(k="%") then goto not_found;
- incr(i);
- end;
-is_ps_name:=true;
-return;
-not_found:is_ps_name:=false;
-exit:end;
-
-@ @<Declare the \ps\ output procedures@>=
-procedure ps_name_out(@!s:str_number;@!lit:boolean);
-begin ps_room(length(s)+2);
-print_char(" ");
-if is_ps_name(s) then
- begin if lit then print_char("/");
- print(s);
- end
- else begin ps_string_out(s);
- if not lit then ps_print("cvx ");
- ps_print("cvn");
- end;
-end;
-
-@ We also need to keep track of which characters are used in text nodes
-in the edge structure that is being shipped out. This is done by procedures
-that use the left-over |b3| field in the |char_info| words; i.e.,
-|char_info(f)(c).b3| gives the status of character |c| in font |f|.
-
-@d unused=0
-@d used=1
-
-@ @<Declare the \ps\ output procedures@>=
-procedure unmark_font(@!f:font_number);
-var @!k:0..font_mem_size; {an index into |font_info|}
-begin for k:= char_base[f]+si(font_bc[f]) to char_base[f]+si(font_ec[f]) do
- font_info[k].qqqq.b3:=unused;
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-procedure mark_string_chars(@!f:font_number;@!s:str_number);
-var @!b:integer; {|char_base[f]|}
-@!bc,@!ec:pool_ASCII_code; {only characters between these bounds are marked}
-@!k:pool_pointer; {an index into string |s|}
-begin b:=char_base[f];
-bc:=si(font_bc[f]);
-ec:=si(font_ec[f]);@/
-k:=str_stop(s);
-while k>str_start[s] do
- begin decr(k);
- if (str_pool[k]>=bc)and(str_pool[k]<=ec) then
- font_info[b+str_pool[k]].qqqq.b3:=used;
- end
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-procedure hex_digit_out(@!d:small_number);
-begin if d<10 then print_char(d+"0")
-else print_char(d+"a"-10);
-end;
-
-@ We output the marks as a hexadecimal bit string starting at |c| or
-|font_bc[f]|, whichever is greater. If the output has to be truncated
-to avoid exceeding |emergency_line_length| the return value says where to
-start scanning next time.
-
-@<Declare the \ps\ output procedures@>=
-function ps_marks_out(@!f:font_number;@!c:eight_bits):halfword;
-var @!bc,@!ec:eight_bits; {only encode characters between these bounds}
-@!lim:integer; {the maximum number of marks to encode before truncating}
-@!p:0..font_mem_size; {|font_info| index for the current character}
-@!d,@!b:0..15; {used to construct a hexadecimal digit}
-begin lim:=4*(emergency_line_length-ps_offset-4);
-bc:=font_bc[f];
-ec:=font_ec[f];
-if c>bc then bc:=c;
-@<Restrict the range |bc..ec| so that it contains no unused characters
- at either end and has length at most |lim|@>;
-@<Print the initial label indicating that the bitmap starts at |bc|@>;
-@<Print a hexadecimal encoding of the marks for characters |bc..ec|@>;
-while (ec<font_ec[f])and(font_info[p].qqqq.b3=unused) do
- begin incr(p); incr(ec);
- end;
-ps_marks_out:=ec+1;
-end;
-
-@ We could save time by setting the return value before the loop that
-decrements |ec|, but there is no point in being so tricky.
-
-@<Restrict the range |bc..ec| so that it contains no unused characters...@>=
-p:=char_base[f]+si(bc);
-while (font_info[p].qqqq.b3=unused)and(bc<ec) do
- begin incr(p); incr(bc);
- end;
-if ec>=bc+lim then ec:=bc+lim-1;
-p:=char_base[f]+si(ec);
-while (font_info[p].qqqq.b3=unused)and(bc<ec) do
- begin decr(p); decr(ec);
- end;
-
-@ @<Print the initial label indicating that the bitmap starts at |bc|@>=
-print_char(" ");
-hex_digit_out(bc div 16);
-hex_digit_out(bc mod 16);
-print_char(":")
-
-@ @<Print a hexadecimal encoding of the marks for characters |bc..ec|@>=
-b:=8; d:=0;
-for p:=char_base[f]+si(bc) to char_base[f]+si(ec) do
- begin if b=0 then
- begin hex_digit_out(d);
- d:=0; b:=8;
- end;
- if font_info[p].qqqq.b3<>unused then d:=d+b;
- b:=halfp(b);
- end;
-hex_digit_out(d)
-
-@ Here is a simple function that determines whether there are any marked
-characters in font~|f| with character code at least~|c|.
-
-@<Declare the \ps\ output procedures@>=
-function check_ps_marks(@!f:font_number; @!c:integer):boolean;
-label exit;
-var @!p:0..font_mem_size; {|font_info| index for the current character}
-begin for p:=char_base[f]+si(c) to char_base[f]+si(font_ec[f]) do
- if font_info[p].qqqq.b3=used then
- begin check_ps_marks:=true; return;
- end;
-check_ps_marks:=false;
-exit: end;
-
-@ There may be many sizes of one font and we need to keep track of the
-characters used for each size. This is done by keeping a linked list of
-sizes for each font with a counter in each text node giving the appropriate
-position in the size list for its font.
-
-@d sc_factor(#)==mem[#+1].sc {the scale factor stored in a font size node}
-@d font_size_size=2 {size of a font size node}
-
-@<Glob...@>=
-font_sizes:array[font_number] of pointer;
-
-@ @d fscale_tolerance==65 {that's $.001\times2^{16}$}
-
-@<Declare the \ps\ output procedures@>=
-function size_index(@!f:font_number;@!s:scaled):quarterword;
-label found;
-var @!p,@!q:pointer; {the previous and current font size nodes}
-@!i:quarterword; {the size index for |q|}
-begin q:=font_sizes[f];
-i:=0;
-while q<>null do
- begin if abs(s-sc_factor(q))<=fscale_tolerance then goto found
- else begin p:=q; q:=link(q);
- incr(i);
- end;
- if i=max_quarterword then
- overflow("sizes per font",max_quarterword);
-@:MetaPost capacity exceeded sizes per font}{\quad sizes per font@>
- end;
-q:=get_node(font_size_size);
-sc_factor(q):=s;
-if i=0 then font_sizes[f]:=q @+else link(p):=q;
-found:size_index:=i;
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-function indexed_size(@!f:font_number;@!j:quarterword):scaled;
-var @!p:pointer; {a font size node}
-@!i:quarterword; {the size index for |p|}
-begin p:=font_sizes[f];
-i:=0;
-if p=null then confusion("size");
-while (i<>j) do
- begin incr(i); p:=link(p);
- if p=null then confusion("size");
- end;
-indexed_size:=sc_factor(p);
-end;
-
-@ @<Declare the \ps\ output procedures@>=
-procedure clear_sizes;
-var @!f:font_number; {the font whose size list is being cleared}
-@!p:pointer; {current font size nodes}
-begin for f:=null_font+1 to last_fnum do
- while font_sizes[f]<>null do
- begin p:=font_sizes[f];
- font_sizes[f]:=link(p);
- free_node(p,font_size_size);
- end;
-end;
-
-@ The \&{special} command saves up lines of text to be printed during the next
-|ship_out| operation. The saved items are stored as a list of capsule tokens.
-
-@<Glob...@>=
-@!last_pending:pointer; {the last token in a list of pending specials}
-
-@ @<Set init...@>=
-last_pending:=spec_head;
-
-@ @<Cases of |do_statement|...@>=
-special_command: if cur_mod=0 then do_special else if cur_mod=1 then do_mapfile else do_mapline;
-
-@ @<Declare action procedures for use by |do_statement|@>=
-procedure do_special;
-begin get_x_next; scan_expression;
-if cur_type<>string_type then @<Complain about improper special operation@>
-else begin link(last_pending):=stash_cur_exp;
- last_pending:=link(last_pending);
- link(last_pending):=null;
- end;
-end;
-
-@ @<Complain about improper special operation@>=
-begin exp_err("Unsuitable expression");
-help1("Only known strings are allowed for output as specials.");
-put_get_error;
-end
-
-@ @<Print any pending specials@>=
-t:=link(spec_head);
-while t<>null do
- begin print(value(t));
- print_ln;
- t:=link(t);
- end;
-flush_token_list(link(spec_head));
-link(spec_head):=null;
-last_pending:=spec_head
-
-@ We are now ready for the main output procedure. Note that the |selector|
-setting is saved in a global variable so that |begin_diagnostic| can access it.
-
-@<Declare the \ps\ output procedures@>=
-procedure ship_out(@!h:pointer); {output edge structure |h|}
-label done,done2,found,found2,found3;
-var @!p:pointer; {the current graphical object}
-@!q:pointer; {something that |p| points to}
-@!t:integer; {a temporary value}
-@!f,ff:font_number; {fonts used in a text node or as loop counters}
-@!ldf:font_number; {the last \.{DocumentFont} listed (otherwise |null_font|)}
-@!done_fonts:boolean; {have we finished listing the fonts in the header?}
-@!next_size:quarterword; {the size index for fonts being listed}
-@!cur_fsize:array[font_number] of pointer; {current positions in |font_sizes|}
-@!ds,@!scf:scaled; {design size and scale factor for a text node}
-@!transformed:boolean; {is the coordinate system being transformed?}
-begin ldf:=null_font; open_output_file;
-non_ps_setting:=selector; selector:=ps_file_only;@/
-if (internal[prologues]=two)or(internal[prologues]=three) then begin
- @<Print improved initial comment and bounding box for edge structure~|h|@>;
- @<Scan all the text nodes and mark the used characters@>;
- mploadencodings(last_fnum);
- @<Update encoding names@>;
- @<Print the improved prologue and setup@>;
- @<Print any pending specials@>;
- unknown_graphics_state(0);
- need_newpath:=true;
- p:=link(dummy_loc(h));
- while p<>null do
- begin if has_color(p) then
- if (pre_script(p))<>null then begin
- print_nl (pre_script(p)); print_ln;
- end;
- fix_graphics_state(p);
- case type(p) of
- @<Cases for translating graphical object~|p| into \ps@>@;
- start_bounds_code,stop_bounds_code: do_nothing;
- end; {all cases are enumerated}
- p:=link(p);
- end;
- print_cmd("showpage","P"); print_ln;
- print("%%EOF"); print_ln;
- a_close(ps_file);
- selector:=non_ps_setting;
- if internal[prologues]<=0 then clear_sizes;
- @<End progress report@>;
- end
-else begin
-@<Print the initial comment and give the bounding box for edge structure~|h|@>;
-if (internal[prologues]>0) and (last_ps_fnum<last_fnum) then
- read_psname_table;
-print("%%BeginProlog"); print_ln;
-if (internal[prologues]>0)or(internal[mpprocset]>0) then @<Print the prologue@>;
-print("%%EndProlog");
-print_nl("%%Page: 1 1"); print_ln;
-@<Print any pending specials@>;
-unknown_graphics_state(0);
-need_newpath:=true;
-p:=link(dummy_loc(h));
-while p<>null do
- begin if has_color(p) then
- if (pre_script(p))<>null then begin
- print_nl (pre_script(p)); print_ln;
- end;
- fix_graphics_state(p);
- case type(p) of
- @<Cases for translating graphical object~|p| into \ps@>@;
- start_bounds_code,stop_bounds_code: do_nothing;
- end; {all cases are enumerated}
- p:=link(p);
- end;
-print_cmd("showpage","P"); print_ln;
-print("%%EOF"); print_ln;
-a_close(ps_file);
-selector:=non_ps_setting;
-if internal[prologues]<=0 then clear_sizes;
-@<End progress report@>;
-end;
-if internal[tracing_output]>0 then print_edges(h," (just shipped out)",true);
-end;
-
-@ @<Print the procset@>=
- begin
- print_nl("/hlw{0 dtransform exch truncate exch idtransform pop setlinewidth}bd");
- print_nl("/vlw{0 exch dtransform truncate idtransform setlinewidth pop}bd");
- print_nl("/l{lineto}bd/r{rlineto}bd/c{curveto}bd/m{moveto}bd/p{closepath}bd/n{newpath}bd");
- print_nl("/C{setcmykcolor}bd/G{setgray}bd/R{setrgbcolor}bd/lj{setlinejoin}bd/ml{setmiterlimit}bd");
- print_nl("/lc{setlinecap}bd/S{stroke}bd/F{fill}bd/q{gsave}bd/Q{grestore}bd/s{scale}bd/t{concat}bd");
- print_nl("/sd{setdash}bd/rd{[] 0 setdash}bd/P{showpage}bd/B{q F Q}bd/W{clip}bd");
- end
-
-
-@
-@d applied_reencoding(#)==((font_is_reencoded(#))and
- ((not font_is_subsetted(#))or(internal[prologues]=two)))
-
-@d ps_print_defined_name(#)==ps_print(" /");
- if (font_is_subsetted(#))and(font_is_included(#))and(internal[prologues]=three)
- then print(fm_font_subset_name(#))
- else print(font_ps_name[#]);
- if str_vs_str(font_name[#],"psyrgo")=0 then begin
- ps_print("-Slanted"); end;
- if str_vs_str(font_name[#],"zpzdr-reversed")=0 then begin
- ps_print("-Reverse"); end;
- if applied_reencoding(#) then begin ps_print("-");
- ps_print(font_enc_name[#]); end;
- if fm_font_slant(#)<>0 then begin
- ps_print("-Slant_"); print_int(fm_font_slant(#)) end;
- if fm_font_extend(#)<>0 then begin
- ps_print("-Extend_"); print_int(fm_font_extend(#)) end
-
-@<Print the improved prologue and setup@>=
-begin
- list_used_resources;
- list_supplied_resources;
- list_needed_resources;
- print_nl("%%EndComments");
- print_nl("%%BeginProlog");
- if internal[mpprocset]>0 then
- print_nl("%%BeginResource: procset mpost")
- else
- print_nl("%%BeginResource: procset mpost-minimal");
- print_nl("/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd");
- if internal[mpprocset]>0 then @<Print the procset@>;
- print_nl("/fcp{findfont dup length dict begin{1 index/FID ne{def}{pop pop}ifelse}forall}bd");
- print_nl("/fmc{FontMatrix dup length array copy dup dup}bd/fmd{/FontMatrix exch def}bd");
- print_nl("/Amul{4 -1 roll exch mul 1000 div}bd/ExtendFont{fmc 0 get Amul 0 exch put fmd}bd");
- if internal[gtroffmode]>0 then begin
- print_nl("/ScaleFont{dup fmc 0 get Amul 0 exch put dup dup 3 get Amul 3 exch put fmd}bd");
- end;
- print_nl("/SlantFont{fmc 2 get dup 0 eq{pop 1}if Amul FontMatrix 0 get mul 2 exch put fmd}bd");
- print_nl("%%EndResource");
- @<Include encodings and fonts for edge structure~|h|@>;
- print_nl("%%EndProlog");
- print_nl("%%BeginSetup");
- print_ln;
- for f:=null_font+1 to last_fnum do begin
- if font_sizes[f]<>null then begin
- if hasfmentry(f) then begin
- @<Write font definition@>;
- ps_name_out(font_name[f],true);
- ps_print_defined_name(f);
- ps_print(" def");
- end
- else begin
- begin_diagnostic;
- if selector=log_only then incr(selector);
- print_err("Warning: font ");
- print(font_name[f]);
- print(" cannot be found in any fontmapfile!");
- end_diagnostic(true);
- ps_name_out(font_name[f],true);
- ps_name_out(font_name[f],true);
- ps_print(" def");
- end;
- print_ln;
- end;
- end;
- print_nl("%%EndSetup");
- print_nl("%%Page: 1 1");
- print_ln;
-end
-
-@ @<Write font definition@>=
-if (applied_reencoding(f))or(fm_font_slant(f)<>0)or(fm_font_extend(f)<>0)or@|
- (str_vs_str(font_name[f],"psyrgo")=0)or(str_vs_str(font_name[f],"zpzdr-reversed")=0) then begin
- if (font_is_subsetted(f))and(font_is_included(f))and(internal[prologues]=three)
- then ps_name_out(fm_font_subset_name(f),true)
- else ps_name_out(font_ps_name[f],true);
- ps_print(" fcp");
- print_ln;
- if applied_reencoding(f) then begin
- ps_print("/Encoding ");
- ps_print(font_enc_name[f]);
- ps_print(" def ");
- end;
- if fm_font_slant(f)<>0 then begin
- print_int(fm_font_slant(f));
- ps_print(" SlantFont ");
- end;
- if fm_font_extend(f)<>0 then begin
- print_int(fm_font_extend(f));
- ps_print(" ExtendFont ");
- end;
- if str_vs_str(font_name[f],"psyrgo")=0 then begin
- ps_print(" 890 ScaleFont ");
- ps_print(" 277 SlantFont ");
- end;
- if str_vs_str(font_name[f],"zpzdr-reversed")=0 then begin
- ps_print(" FontMatrix [-1 0 0 1 0 0] matrix concatmatrix /FontMatrix exch def ");
- ps_print("/Metrics 2 dict dup begin ");
- ps_print("/space[0 -278]def ");
- ps_print("/a12[-904 -939]def ");
- ps_print("end def ");
- end; ps_print("currentdict end");
- print_ln;
- ps_print_defined_name(f);
- ps_print(" exch definefont pop");
- print_ln;
-end
-
-@ Included subset fonts do not need and encoding vector, make
-sure we skip that case.
-
-@p procedure list_used_resources;
-label found,found2;
-var @!f,ff:font_number; {fonts used in a text node or as loop counters}
-@!ldf:font_number; {the last \.{DocumentFont} listed (otherwise |null_font|)}
-firstitem:boolean;
-begin
-if internal[mpprocset]>0 then
- print_nl("%%DocumentResources: procset mpost")
-else
- print_nl("%%DocumentResources: procset mpost-minimal");
-ldf:=null_font;
-firstitem:=true;
-for f:=null_font+1 to last_fnum do
- if (font_sizes[f]<>null)and(font_is_reencoded(f)) then
- begin
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_enc_name[f],font_enc_name[ff])=0 then
- goto found;
- if font_is_subsetted(f) then
- goto found;
- if ps_offset+1+length(font_enc_name[f])>max_print_line then
- print_nl("%%+ encoding");
- if firstitem then begin
- firstitem:=false;
- print_nl("%%+ encoding");
- end;
- print_char(" ");
- print(font_enc_name[f]);
- ldf:=f;
- found:
- end;
-ldf:=null_font;
-firstitem:=true;
-for f:=null_font+1 to last_fnum do
- if font_sizes[f]<>null then
- begin
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_name[f],font_name[ff])=0 then
- goto found2;
- if ps_offset+1+length(font_ps_name[f])>max_print_line then
- print_nl("%%+ font");
- if firstitem then begin
- firstitem:=false;
- print_nl("%%+ font");
- end;
- print_char(" ");
- if (internal[prologues]=three)and(font_is_subsetted(f)) then
- print(fm_font_subset_name(f))
- else
- print(font_ps_name[f]);
- ldf:=f;
- found2:
- end;
-print_ln;
-end;
-
-@
-@p procedure list_supplied_resources;
-label found,found2;
-var @!f,ff:font_number; {fonts used in a text node or as loop counters}
-@!ldf:font_number; {the last \.{DocumentFont} listed (otherwise |null_font|)}
-firstitem:boolean;
-begin
-if internal[mpprocset]>0 then
- print_nl("%%DocumentSuppliedResources: procset mpost")
-else
- print_nl("%%DocumentSuppliedResources: procset mpost-minimal");
-ldf:=null_font;
-firstitem:=true;
-for f:=null_font+1 to last_fnum do
- if (font_sizes[f]<>null)and(font_is_reencoded(f)) then
- begin
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_enc_name[f],font_enc_name[ff])=0 then
- goto found;
- if (internal[prologues]=three)and(font_is_subsetted(f))then
- goto found;
- if ps_offset+1+length(font_enc_name[f])>max_print_line then
- print_nl("%%+ encoding");
- if firstitem then begin
- firstitem:=false;
- print_nl("%%+ encoding");
- end;
- print_char(" ");
- print(font_enc_name[f]);
- ldf:=f;
- found:
- end;
-ldf:=null_font;
-firstitem:=true;
-if internal[prologues]=three then begin
- for f:=null_font+1 to last_fnum do
- if font_sizes[f]<>null then
- begin
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_name[f],font_name[ff])=0 then
- goto found2;
- if not font_is_included(f) then
- goto found2;
- if ps_offset+1+length(font_ps_name[f])>max_print_line then
- print_nl("%%+ font");
- if firstitem then begin
- firstitem:=false;
- print_nl("%%+ font");
- end;
- print_char(" ");
- if font_is_subsetted(f) then
- print(fm_font_subset_name(f))
- else
- print(font_ps_name[f]);
- ldf:=f;
- found2:
- end;
- print_ln;
-end;
-end;
-
-
-@
-@p procedure list_needed_resources;
-label found,found2;
-var @!f,ff:font_number; {fonts used in a text node or as loop counters}
-@!ldf:font_number; {the last \.{DocumentFont} listed (otherwise |null_font|)}
-firstitem:boolean;
-begin
-ldf:=null_font;
-firstitem:=true;
-for f:=null_font+1 to last_fnum do
- if font_sizes[f]<>null then
- begin
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_name[f],font_name[ff])=0 then
- goto found;
- if(internal[prologues]=three)and(font_is_included(f)) then
- goto found;
- if ps_offset+1+length(font_ps_name[f])>max_print_line then
- print_nl("%%+ font");
- if firstitem then begin
- firstitem:=false;
- print_nl("%%DocumentNeededResources: font");
- end;
- print_char(" ");
- print(font_ps_name[f]);
- ldf:=f;
- found:
- end;
-if not firstitem then begin
- print_ln;
- ldf:=null_font;
- firstitem:=true;
- for f:=null_font+1 to last_fnum do
- if font_sizes[f]<>null then
- begin
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_name[f],font_name[ff])=0 then
- goto found2;
- if(internal[prologues]=three)and(font_is_included(f)) then
- goto found2;
- print("%%IncludeResource: font ");
- print(font_ps_name[f]);
- print_ln;
- ldf:=f;
- found2:
- end;
-end;
-end;
-
-@
-@<Print improved initial comment and bounding box for edge...@>=
-print("%!PS-Adobe-3.0 EPSF-3.0");
-print_nl("%%BoundingBox: ");
-set_bbox(h,true);
-if minx_val(h)>maxx_val(h) then print("0 0 0 0")
-else begin ps_pair_out(floor_scaled(minx_val(h)),floor_scaled(miny_val(h)));
- ps_pair_out(-floor_scaled(-maxx_val(h)),-floor_scaled(-maxy_val(h)));
- end;
-print_nl("%%HiResBoundingBox: ");
-if minx_val(h)>maxx_val(h) then print("0 0 0 0")
-else begin
- ps_pair_out(minx_val(h),miny_val(h));
- ps_pair_out(maxx_val(h),maxy_val(h));
- end;
-print_nl("%%Creator: MetaPost ");
-print(metapost_version);
-print_nl("%%CreationDate: ");
-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);@/
-print_nl("%%Pages: 1");@/
-
-@
-
-@ @<Include encodings and fonts for edge structure~|h|@>=
-mpfontencodings(last_fnum,(internal[prologues]=two));
-@<Embed fonts that are available@>
-
-@ @<Scan all the text nodes and mark the used ...@>=
-for f:=null_font+1 to last_fnum do begin
- if font_sizes[f]<>null then begin
- unmark_font(f);
- font_sizes[f]:=null;
- end;
- if font_enc_name[f]<>0 then
- delete_str_ref(font_enc_name[f]);
- font_enc_name[f] := 0;
-end;
-for f:=null_font+1 to last_fnum do begin
- p:=link(dummy_loc(h));
- while p<>null do
- begin if type(p)=text_code then
- if font_n(p)<>null_font then begin
- font_sizes[font_n(p)] := void;
- mark_string_chars(font_n(p),text_p(p));
- if hasfmentry(font_n(p)) then
- font_ps_name[font_n(p)] := fm_font_name(font_n(p));
- end;
- p:=link(p);
- end;
-end
-
-@ @<Update encoding names@>=
-for f:=null_font+1 to last_fnum do begin
- p:=link(dummy_loc(h));
- while p<>null do
- begin if type(p)=text_code then
- if font_n(p)<>null_font then
- if hasfmentry(font_n(p)) then
- if font_enc_name[font_n(p)]=0 then
- font_enc_name[font_n(p)] := fm_encoding_name(font_n(p));
- p:=link(p);
- end;
-end
-
-@ @<Embed fonts that are available@>=
-begin next_size:=0;
-@<Make |cur_fsize| a copy of the |font_sizes| array@>;
-repeat done_fonts:=true;
-for f:=null_font+1 to last_fnum do
- begin if cur_fsize[f]<>null then begin
- if internal[prologues]=three then
- if not dopsfont(f) then
- if hasfmentry(f) then begin
- print_err("Font embedding failed");
- error;
- end;
- cur_fsize[f]:=link(cur_fsize[f]);
- if cur_fsize[f]<>null then
- begin unmark_font(f); done_fonts:=false; @+end
- end;
- end;
- if not done_fonts then
- @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
- that size index@>;
- until done_fonts;
-end
-
-
-@ These special comments described in the {\sl PostScript Language Reference
-Manual}, 2nd.~edition are understood by some \ps-reading programs.
-We can't normally output ``conforming'' \ps\ because
-the structuring conventions don't allow us to say ``Please make sure the
-following characters are downloaded and define the \.{fshow} macro to access
-them.''
-
-The exact bounding box is written out if |prologues<0|, although this
-is not standard \ps, since it allows \TeX\ to calculate the box dimensions
-accurately. (Overfull boxes are avoided if an illustration is made to
-match a given \.{\char`\\hsize}.)
-
-@<Print the initial comment and give the bounding box for edge...@>=
-print("%!PS");
-if internal[prologues]>0 then print("-Adobe-3.0 EPSF-3.0");
-print_nl("%%BoundingBox: ");
-set_bbox(h,true);
-if minx_val(h)>maxx_val(h) then print("0 0 0 0")
-else if internal[prologues]<0 then
- begin ps_pair_out(minx_val(h),miny_val(h));
- ps_pair_out(maxx_val(h),maxy_val(h));
- end
-else begin ps_pair_out(floor_scaled(minx_val(h)),floor_scaled(miny_val(h)));
- ps_pair_out(-floor_scaled(-maxx_val(h)),-floor_scaled(-maxy_val(h)));
- end;
-print_nl("%%HiResBoundingBox: ");
-if minx_val(h)>maxx_val(h) then print("0 0 0 0")
-else begin
- ps_pair_out(minx_val(h),miny_val(h));
- ps_pair_out(maxx_val(h),maxy_val(h));
- end;
-print_nl("%%Creator: MetaPost ");
-print(metapost_version);
-print_nl("%%CreationDate: ");
-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);@/
-print_nl("%%Pages: 1");@/
-@<List all the fonts and magnifications for edge structure~|h|@>;
-print_ln
-
-@ @<List all the fonts and magnifications for edge structure~|h|@>=
-@<Scan all the text nodes and set the |font_sizes| lists;
- if |internal[prologues]<=0| list the sizes selected by |choose_scale|,
- apply |unmark_font| to each font encountered, and call |mark_string|
- whenever the size index is zero@>;
-if internal[prologues]>0 then
- @<Give a \.{DocumentFonts} comment listing all fonts with non-null
- |font_sizes| and eliminate duplicates@>
-else begin next_size:=0;
- @<Make |cur_fsize| a copy of the |font_sizes| array@>;
- repeat done_fonts:=true;
- for f:=null_font+1 to last_fnum do
- begin if cur_fsize[f]<>null then
- @<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>;
- if cur_fsize[f]<>null then
- begin unmark_font(f); done_fonts:=false; @+end;
- end;
- if not done_fonts then
- @<Increment |next_size| and apply |mark_string_chars| to all text nodes with
- that size index@>;
- until done_fonts;
- end
-
-@ @<Make |cur_fsize| a copy of the |font_sizes| array@>=
-for f:=null_font+1 to last_fnum do
- cur_fsize[f]:=font_sizes[f]
-
-@ It's not a good idea to make any assumptions about the |font_ps_name| entries,
-so we carefully remove duplicates. There is no harm in using a slow, brute-force
-search.
-
-@<Give a \.{DocumentFonts} comment listing all fonts with non-null...@>=
-begin ldf:=null_font;
-for f:=null_font+1 to last_fnum do
- if font_sizes[f]<>null then
- begin if ldf=null_font then print_nl("%%DocumentFonts:");
- for ff:=ldf downto null_font do
- if font_sizes[ff]<>null then
- if str_vs_str(font_ps_name[f],font_ps_name[ff])=0 then
- goto found;
- if ps_offset+1+length(font_ps_name[f])>max_print_line then
- print_nl("%%+");
- print_char(" ");
- print(font_ps_name[f]);
- ldf:=f;
- found:
- end;
-end
-
-@ @<Scan all the text nodes and set the |font_sizes| lists;...@>=
-for f:=null_font+1 to last_fnum do font_sizes[f]:=null;
-p:=link(dummy_loc(h));
-while p<>null do
- begin if type(p)=text_code then
- if font_n(p)<>null_font then
- begin f:=font_n(p);
- if internal[prologues]>0 then font_sizes[f]:=void
- else begin if font_sizes[f]=null then unmark_font(f);
- name_type(p):=size_index(f,choose_scale(p));
- if name_type(p)=0 then
- mark_string_chars(f,text_p(p));
- end;
- end;
- p:=link(p);
- end
-
-@ If the file name is so long that it can't be printed without exceeding
-|emergency_line_length| then there will be missing items in the \.{\%*Font:}
-line. We might have to repeat line in order to get the character usage
-information to fit within |emergency_line_length|.
-
-@<Print the \.{\%*Font} comment for font |f| and advance |cur_fsize[f]|@>=
-begin t:=0;
-while check_ps_marks(f,t) do
- begin print_nl("%*Font: ");
- if ps_offset+length(font_name[f])+12>emergency_line_length then
- goto done;
- print(font_name[f]);
- print_char(" ");
- ds:=(font_dsize[f] + 8) div 16;
- print_scaled(take_scaled(ds,sc_factor(cur_fsize[f])));
- if ps_offset+12>emergency_line_length then goto done;
- print_char(" ");
- print_scaled(ds);
- if ps_offset+5>emergency_line_length then goto done;
- t:=ps_marks_out(f,t);
- end;
-done:
-cur_fsize[f]:=link(cur_fsize[f]);
-end
-
-@ @<Increment |next_size| and apply |mark_string_chars| to all text nodes...@>=
-begin incr(next_size);
-p:=link(dummy_loc(h));
-while p<>null do
- begin if type(p)=text_code then
- if font_n(p)<>null_font then
- if name_type(p)=next_size then
- mark_string_chars(font_n(p),text_p(p));
- p:=link(p);
- end;
-end
-
-@ The prologue defines \.{fshow} and corrects for the fact that \.{fshow}
-arguments use |font_name| instead of |font_ps_name|. Downloaded bitmap fonts
-might not have reasonable |font_ps_name| entries, but we just charge ahead
-anyway. The user should not make \&{prologues} positive if this will cause
-trouble.
-@:prologues_}{\&{prologues} primitive@>
-
-@<Print the prologue@>=
-begin
-if ldf<>null_font then begin
- if internal[prologues]>0 then begin
- for f:=null_font+1 to last_fnum do
- if font_sizes[f]<>null then
- begin ps_name_out(font_name[f],true);
- ps_name_out(font_ps_name[f],true);
- ps_print(" def");
- print_ln;
- end;
- if internal[mpprocset]=0 then begin
- print("/fshow {exch findfont exch scalefont setfont show}bind def");
- print_ln;
- end;
- end;
- end;
-if internal[mpprocset]>0 then begin
- print_nl("%%BeginResource: procset mpost");
- if (internal[prologues]>0)and(ldf<>null_font) then
- print_nl("/bd{bind def}bind def/fshow {exch findfont exch scalefont setfont show}bd")
- else
- print_nl("/bd{bind def}bind def");
- @<Print the procset@>;
- print_nl("%%EndResource");
- print_ln;
- end;
-end
-
-@ @<Cases for translating graphical object~|p| into \ps@>=
-start_clip_code:begin print_nl(""); print_cmd("gsave ","q ");
- ps_path_out(path_p(p));
- ps_print_cmd(" clip"," W");
- print_ln;
- if internal[restore_clip_color]>0 then
- unknown_graphics_state(1);
- end;
-stop_clip_code:begin print_nl(""); print_cmd("grestore","Q");
- print_ln;
- if internal[restore_clip_color]>0 then
- unknown_graphics_state(2)
- else
- unknown_graphics_state(-1);
- end;
-
-@ @<Cases for translating graphical object~|p| into \ps@>=
-fill_code: begin
- if pen_p(p)=null then ps_fill_out(path_p(p))
- else if pen_is_elliptical(pen_p(p)) then stroke_ellipse(p,true)
- else begin do_outer_envelope(copy_path(path_p(p)), p);
- do_outer_envelope(htap_ypoc(path_p(p)), p);
- end;
- if (post_script(p))<>null then begin
- print_nl (post_script(p)); print_ln;
- end;
- end;
-stroked_code: begin
- if pen_is_elliptical(pen_p(p)) then stroke_ellipse(p,false)
- else begin q:=copy_path(path_p(p));
- t:=lcap_val(p);
- @<Break the cycle and set |t:=1| if path |q| is cyclic@>;
- q:=make_envelope(q,pen_p(p),ljoin_val(p),t,miterlim_val(p));
- ps_fill_out(q);
- toss_knot_list(q);
- end;
- if (post_script(p))<>null then begin
- print_nl (post_script(p)); print_ln;
- end;
- end;
-
-@ The envelope of a cyclic path~|q| could be computed by calling
-|make_envelope| once for |q| and once for its reversal. We don't do this
-because it would fail color regions that are covered by the pen regardless
-of where it is placed on~|q|.
-
-@<Break the cycle and set |t:=1| if path |q| is cyclic@>=
-if left_type(q)<>endpoint then
- begin left_type(insert_knot(q,x_coord(q),y_coord(q))):=endpoint;
- right_type(q):=endpoint;
- q:=link(q);
- t:=1;
- end
-
-@ @<Cases for translating graphical object~|p| into \ps@>=
-text_code: begin
- if (font_n(p)<>null_font) and (length(text_p(p))>0) then
- begin if internal[prologues]>0 then
- scf:=choose_scale(p)
- else scf:=indexed_size(font_n(p), name_type(p));
- @<Shift or transform as necessary before outputting text node~|p| at scale
- factor~|scf|; set |transformed:=true| if the original transformation must
- be restored@>;
- ps_string_out(text_p(p));
- ps_name_out(font_name[font_n(p)],false);
- @<Print the size information and \ps\ commands for text node~|p|@>;
- print_ln;
- end;
- if (post_script(p))<>null then begin
- print_nl (post_script(p)); print_ln;
- end;
- end;
-
-@ @<Print the size information and \ps\ commands for text node~|p|@>=
-ps_room(18);
-print_char(" ");
-ds:=(font_dsize[font_n(p)]+8) div 16;
-print_scaled(take_scaled(ds,scf));
-print(" fshow");
-if transformed then ps_print_cmd(" grestore"," Q")
-
-@ @<Shift or transform as necessary before outputting text node~|p| at...@>=
-transformed:=(txx_val(p)<>scf)or(tyy_val(p)<>scf)or@|
- (txy_val(p)<>0)or(tyx_val(p)<>0);
-if transformed then
- begin print_cmd("gsave [", "q [");
- ps_pair_out(make_scaled(txx_val(p),scf),@|make_scaled(tyx_val(p),scf));
- ps_pair_out(make_scaled(txy_val(p),scf),@|make_scaled(tyy_val(p),scf));
- ps_pair_out(tx_val(p),ty_val(p));@/
- ps_print_cmd("] concat 0 0 moveto","] t 0 0 m");
- end
-else begin ps_pair_out(tx_val(p),ty_val(p));
- ps_print_cmd("moveto","m");
- end;
-print_ln
-
-@ 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.
-
-@ @<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
-
-@* \[45] Dumping and undumping the tables.
-After \.{INIMP} has seen a collection of macros, it
-can write all the necessary information on an auxiliary file so
-that production versions of \MP\ 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.
-@.INIMP@>
-
-The global variable |mem_ident| is a string that is printed right
-after the |banner| line when \MP\ is ready to start. For \.{INIMP} this
-string says simply `\.{(INIMP)}'; for other versions of \MP\ it says,
-for example, `\.{(preloaded mem=plain 1990.4.14)}', showing the year,
-month, and day that the mem file was created. We have |mem_ident=0|
-before \MP's tables are loaded.
-
-@<Glob...@>=
-@!mem_ident:str_number;
-
-@ @<Set init...@>=
-mem_ident:=0;
-
-@ @<Initialize table entries...@>=
-mem_ident:=" (INIMP)";
-
-@ @<Declare act...@>=
-@!init procedure store_mem_file;
-label done;
-var @!k:integer; {all-purpose index}
-@!p,@!q: pointer; {all-purpose pointers}
-@!x: integer; {something to dump}
-@!w: four_quarters; {four ASCII codes}
-@!s: str_number; {all-purpose string}
-begin @<Create the |mem_ident|, open the mem 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 mem file@>;
-end;
-tini
-
-@ Corresponding to the procedure that dumps a mem file, we also have a function
-that reads~one~in. The function returns |false| if the dumped mem is
-incompatible with the present \MP\ table sizes, etc.
-
-@d off_base=6666 {go here if the mem 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_mem_file|@>@;
-function load_mem_file:boolean;
-label done,off_base,exit;
-var @!k:integer; {all-purpose index}
-@!p,@!q: pointer; {all-purpose pointers}
-@!x: integer; {something undumped}
-@!s: str_number; {some temporary string}
-@!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_mem_file:=true; return; {it worked!}
-off_base: wake_up_terminal;
- wterm_ln('(Fatal mem file error; I''m stymied)');
-@.Fatal mem file error@>
-load_mem_file:=false;
-exit:end;
-
-@ Mem files consist of |memory_word| items, and we use the following
-macros to dump words of different types:
-
-@d dump_wd(#)==begin mem_file^:=#; put(mem_file);@+end
-@d dump_int(#)==begin mem_file^.int:=#; put(mem_file);@+end
-@d dump_hh(#)==begin mem_file^.hh:=#; put(mem_file);@+end
-@d dump_qqqq(#)==begin mem_file^.qqqq:=#; put(mem_file);@+end
-
-@<Glob...@>=
-@!mem_file:word_file; {for input or output of mem 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(mem_file); #:=mem_file^;@+end
-@d undump_int(#)==begin get(mem_file); #:=mem_file^.int;@+end
-@d undump_hh(#)==begin get(mem_file); #:=mem_file^.hh;@+end
-@d undump_qqqq(#)==begin get(mem_file); #:=mem_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 \.{INIMP} and \MP\ 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:=mem_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
-
-@ We do string pool compaction to avoid dumping unused strings.
-
-@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@>=
-do_compaction(pool_size);
-dump_int(pool_ptr);
-dump_int(max_str_ptr);
-dump_int(str_ptr);
-k:=0;
-while (next_str[k]=k+1) and (k<=max_str_ptr) do incr(k);
-dump_int(k);
-while k<=max_str_ptr do
- begin dump_int(next_str[k]); incr(k);
- end;
-k:=0;
-loop @+begin dump_int(str_start[k]);
- if k=str_ptr then goto done else k:=next_str[k];
- end;
-done: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("at most "); print_int(max_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-1)('max strings')(max_str_ptr);
-undump(0)(max_str_ptr)(str_ptr);
-undump(0)(max_str_ptr+1)(s);
-for k:=0 to s-1 do next_str[k]:=k+1;
-for k:=s to max_str_ptr do undump(s+1)(max_str_ptr+1)(next_str[k]);
-fixed_str_use:=0;
-k:=0;
-loop @+begin undump(0)(pool_ptr)(str_start[k]);
- if k=str_ptr then goto done;
- str_ref[k]:=max_str_ref;
- incr(fixed_str_use);
- last_fixed_str:=k; k:=next_str[k];
- end;
-done: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_use:=fixed_str_use; init_pool_ptr:=pool_ptr;
-max_pool_ptr:=pool_ptr;
-strs_used_up:=fixed_str_use;
-stat pool_in_use:=str_start[str_ptr]; strs_in_use:=fixed_str_use;
- max_pl_used:=pool_in_use; max_strs_used:=strs_in_use;@/
- pact_count:=0; pact_chars:=0; pact_strs:=0;
-tats
-
-@ 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 \.{INIMP} 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 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(mem_ident);
-dump_int(bg_loc); dump_int(eg_loc); dump_int(serial_no); dump_int(69073);
-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)(mem_ident);
-undump(1)(hash_end)(bg_loc);
-undump(1)(hash_end)(eg_loc);
-undump_int(serial_no);@/
-undump_int(x);@+if (x<>69073)or eof(mem_file) then goto off_base
-
-@ @<Create the |mem_ident|...@>=
-selector:=new_string;
-print(" (preloaded mem="); print(job_name); print_char(" ");
-print_int(round_unscaled(internal[year]) mod 100); 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); mem_ident:=make_string; str_ref[mem_ident]:=max_str_ref;@/
-pack_job_name(mem_extension);
-while not w_open_out(mem_file) do
- prompt_file_name("mem file name",mem_extension);
-print_nl("Beginning to dump on file ");
-@.Beginning to dump...@>
-s:=w_make_name_string(mem_file);
-print(s); flush_string(s);
-print_nl(mem_ident)
-
-@ @<Close the mem file@>=
-w_close(mem_file)
-
-@* \[46] The main program.
-This is it: the part of \MP\ 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 \MP. One, called \.{INIMP},
-@.INIMP@>
-has to be run first; it initializes everything from scratch, without
-reading a mem file, and it has the capability of dumping a mem file.
-The other one is called `\.{VIRMP}'; it is a ``virgin'' program that needs
-@.VIRMP@>
-to input a mem file in order to get started. \.{VIRMP} typically has
-a bit more memory capacity than \.{INIMP}, because it does not need the
-space consumed by the dumping/undumping routines and the numerous calls on
-|primitive|, etc.
-
-The \.{VIRMP} program cannot read a mem file instantaneously, of course;
-the best implementations therefore allow for production versions of \MP\ that
-not only avoid the loading routine for \PASCAL\ object code, they also have
-a mem 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 \.{VIRMP} is first
-loaded.\quad(2)~After we have read in a mem file and initialized
-everything, we set |ready_already:=314159|.\quad(3)~Soon \.{VIRMP}
-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@>
-
-@<Glob...@>=
-@!ready_already:integer; {a sacrifice of purity for economy}
-
-@ Now this is really it: \MP\ 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_MP;
-@<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_use:=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_MP: @<Initialize the output routines@>;
-@<Get the first line of input and prepare to start@>;
-mp_init_map_file(troff_mode);
-setjobid(internal[year],internal[month],internal[day],internal[time]);
-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_MP: close_files_and_terminate;
-final_end: ready_already:=0;
-end.
-
-@ Here we do whatever is needed to complete \MP'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}
-begin @<Close all open files in the |rd_file| and |wr_file| arrays@>;
-@!stat if internal[tracing_stats]>0 then
- @<Output statistics about this job@>;@;@+tats@/
-wake_up_terminal; @<Do all the finishing work on the \.{TFM} file@>;
-@<Explain what output files were written@>;
-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...@>
- print(log_name); print_char(".");
- end;
- end;
-end;
-
-@ @<Close all open files in the |rd_file| and |wr_file| arrays@>=
-for k:=0 to read_files-1 do
- if rd_fname[k]<>0 then a_close(rd_file[k]);
-for k:=0 to write_files-1 do
- if wr_fname[k]<>0 then a_close(wr_file[k])
-
-@ We want to produce a \.{TFM} file if and only if |fontmaking| is positive.
-
-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.
-
-@<Do all the finishing work on the \.{TFM} file@>=
-if internal[fontmaking]>0 then
- begin @<Make the dynamic memory into one big available node@>;
- @<Massage the \.{TFM} widths@>;
- fix_design_size; fix_check_sum;
- @<Massage the \.{TFM} heights, depths, and italic corrections@>;
- internal[fontmaking]:=0; {avoid loop in case of fatal error}
- @<Finish the \.{TFM} 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 \MP\ is being used.
-
-@<Output statistics...@>=
-if log_opened then
- begin wlog_ln(' ');
- wlog_ln('Here is how much of MetaPost''s memory',' you used:');
-@.Here is how much...@>
- wlog(' ',max_strs_used-init_str_use:1,' string');
- if max_strs_used<>init_str_use+1 then wlog('s');
- wlog_ln(' out of ', max_strings-1-init_str_use:1);@/
- wlog_ln(' ',max_pl_used-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_param_stack:1,'p,',@|
- max_buf_stack+1:1,'b stack positions out of ',@|
- stack_size:1,'i,',
- max_internal:1,'n,',
- param_size:1,'p,',
- buf_size:1,'b');
- wlog_ln(' ',pact_count:1,' string compactions (moved ',
- pact_chars:1,' characters, ',
- pact_strs:1,' strings)');
- 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); cond_ptr:=link(cond_ptr);
- 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_mem_file; return;@+tini@/
- print_nl("(dump is performed only by INIMP)"); return;
-@.dump...only by INIMP@>
- 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 \.{INIMP} only)@>@;
-end;
-tini
-
-@ When we begin the following code, \MP'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, \MP\ is ready to call on the
-|main_control| routine to do its work.
-
-@<Get the first line...@>=
-begin @<Initialize the input routines@>;
-if (mem_ident=0)or(buffer[loc]="&") then
- begin if mem_ident<>0 then initialize; {erase preloaded mem}
- if not open_mem_file then goto final_end;
- if not load_mem_file then
- begin w_close(mem_file); goto final_end;
- end;
- w_close(mem_file);
- while (loc<limit)and(buffer[loc]=" ") do incr(loc);
- end;
-buffer[limit]:="%";@/
-fix_date_and_time;@/
-sys_random_seed := (internal[time] div unity)+internal[day];@/
-init_randoms(sys_random_seed);@/
-@<Initialize the print |selector|...@>;
-if loc<limit then if buffer[loc]<>"\" then start_input; {\&{input} assumed}
-end
-
-@* \[47] Debugging.
-Once \MP\ 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 \MP\ 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 \MP\ 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: 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;
-
-
-@ \MP\ used to have one single routine to print to both `write' files
-and the PostScript output. Web2c redefines ``Character |k| cannot be
-printed'', and that resulted in some bugs where 8-bit characters were
-written to the PostScript file (reported by Wlodek Bzyl).
-
-Also, Hans Hagen requested spaces to be output as "\\040" instead of
-a plain space, since that makes it easier to parse the result file
-for postprocessing.
-
-@<Character |k| is not allowed in PostScript output@>=
- (k<=" ")or(k>"~")
-
-@ Saving the filename template
-
-@<Save the filename template@>=
-begin if filename_template<>0 then delete_str_ref(filename_template);
-if length(cur_exp)=0 then filename_template:=0
-else begin filename_template:=cur_exp; add_str_ref(filename_template);
- end;
-end
-@* \[48] System-dependent changes.
-This section should be replaced, if necessary, by any special
-modification of the program
-that are necessary to make \MP\ 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@>
-
-@* \[49] 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 \MP\ 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 \MP's running time, exclusive of input and output.
diff --git a/Build/source/texk/web2c/mplibdir/ChangeLog b/Build/source/texk/web2c/mplibdir/ChangeLog
index ed97ac67f76..58c2d5aa0c2 100644
--- a/Build/source/texk/web2c/mplibdir/ChangeLog
+++ b/Build/source/texk/web2c/mplibdir/ChangeLog
@@ -1,5 +1,10 @@
2009-05-25 Peter Breitenlohner <peb@mppmu.mpg.de>
+ * dvitomp.test (new): Shell script to test dvitomp.
+ * am/mplib.am: Added dvitomp.test.
+
+2009-05-25 Peter Breitenlohner <peb@mppmu.mpg.de>
+
* am/.am (install-exec-hook, uninstall-hook): new targets
to (un)install dvitomp symlink.
diff --git a/Build/source/texk/web2c/mplibdir/am/mplib.am b/Build/source/texk/web2c/mplibdir/am/mplib.am
index 891a9ef3fec..bdd46e7af0d 100644
--- a/Build/source/texk/web2c/mplibdir/am/mplib.am
+++ b/Build/source/texk/web2c/mplibdir/am/mplib.am
@@ -47,7 +47,7 @@ uninstall-hook::
## MetaPost Tests
##
-mp_tests = mplibdir/mptrap.test
+mp_tests = mplibdir/dvitomp.test mplibdir/mptrap.test
EXTRA_DIST += $(mp_tests)
@@ -58,6 +58,8 @@ endif MP
.PHONY: mptrap-clean
clean-local:: mptrap-clean
+## dvitomp.test
+DISTCLEANFILES += ptmr.mpx tests/xptmr.mpx tests/xstory.mpx
## mptrap.test
mptrap-clean:
rm -f mtrap.mp mtrap.mem trapf.tfm
diff --git a/Build/source/texk/web2c/dvitomp.test b/Build/source/texk/web2c/mplibdir/dvitomp.test
index 834c0f42c14..c2f91463f96 100755
--- a/Build/source/texk/web2c/dvitomp.test
+++ b/Build/source/texk/web2c/mplibdir/dvitomp.test
@@ -9,13 +9,13 @@ if tfmpath=`$KPSEWHICH cmbx10.tfm`; then
TEXMFCNF=$srcdir/../kpathsea \
TFMFONTS=`dirname $tfmpath` \
- ./dvitomp $srcdir/tests/story.dvi tests/xstory.mpx || exit 1
+ ./mpost --dvitomp $srcdir/tests/story.dvi tests/xstory.mpx || exit 1
fi
TEXMFCNF=$srcdir/../kpathsea \
TFMFONTS=$srcdir/tests \
VFFONTS=$srcdir/tests: \
- ./dvitomp $srcdir/tests/ptmr && \
+ ./mpost --dvitomp $srcdir/tests/ptmr ptmr.mpx && \
mv ptmr.mpx tests/xptmr.mpx || exit 1