diff options
Diffstat (limited to 'Build/source/texk/web2c')
178 files changed, 50161 insertions, 1373 deletions
diff --git a/Build/source/texk/web2c/ChangeLog b/Build/source/texk/web2c/ChangeLog index c9203566413..b34327fb6fe 100644 --- a/Build/source/texk/web2c/ChangeLog +++ b/Build/source/texk/web2c/ChangeLog @@ -1,3 +1,64 @@ +2024-09-16 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * tests/ec-lmr10.tfm, tests/dump-basic.tex: + Add for tests of pTeX family + +2024-09-07 Karl Berry <karl@freefriends.org> + + * Makefile.am (texprof.am): include. + * ac/web2c.ac (kpse_tex_progs) <texprof>: new entry in table. + +2024-09-06 Andreas Scherer <https://ascherer.github.io> + + * weav-twill.ch, + * weave.ch: Sort section names in TWILL. + +2024-09-02 Andreas Scherer <https://ascherer.github.io> + + * bibtex.ch, + * dvitype.ch, + * gftodvi.ch, + * gftopk.ch, + * gftype.ch, + * mft.ch, + * pktype.ch, + * pltotf.ch, + * pooltype.ch, + * tangle.ch, + * tftopl.ch, + * vftovp.ch, + * vptovf.ch, + * weave.ch: Flip two section names in the index. + * dvicopy.ch, + * dvicopy.web, + * patgen.ch, + * patgen.web, + * pktogf.ch, + * pktogf.web: Fix sorting order in 'other' WEB codees. + +2024-08-31 Andreas Scherer <https://ascherer.github.io> + + * vftovp.ch: Fix sorting order of new named sections. + +2024-08-23 Andreas Scherer <https://ascherer.github.io> + + * gftopk.ch, + * gftype.ch, + * pktogf.ch, + * pktype.ch: Fix a few spaces. + +2024-08-21 Andreas Scherer <https://ascherer.github.io> + + * weav-twill.ch: Prime the title page. + +2024-08-16 Andreas Scherer <https://ascherer.github.io> + + * showstream.ch: Restore section numbering as far as possible. + +2024-08-12 Andreas Scherer <https://ascherer.github.io> + + * ctangleboot.cin: CWEB 4.12 release. + 2024-07-14 Andreas Scherer <https://ascherer.github.io> * mft.ch: Preserve Metafont logo on title page. diff --git a/Build/source/texk/web2c/Makefile.am b/Build/source/texk/web2c/Makefile.am index 6e271293a66..183082f7e22 100644 --- a/Build/source/texk/web2c/Makefile.am +++ b/Build/source/texk/web2c/Makefile.am @@ -299,6 +299,9 @@ include $(srcdir)/luatexdir/am/luatex.am ## luahbTeX include $(srcdir)/luatexdir/am/luaharfbuzz.am +## texprof +include $(srcdir)/texprofdir/am/texprof.am + ## XeTeX include $(srcdir)/xetexdir/am/xetex.am diff --git a/Build/source/texk/web2c/Makefile.in b/Build/source/texk/web2c/Makefile.in index 9a762efeef9..0edf625113b 100644 --- a/Build/source/texk/web2c/Makefile.in +++ b/Build/source/texk/web2c/Makefile.in @@ -115,22 +115,22 @@ bin_PROGRAMS = tangle$(EXEEXT) ctangle$(EXEEXT) $(am__EXEEXT_4) \ $(am__EXEEXT_20) $(am__EXEEXT_21) $(am__EXEEXT_22) \ $(am__EXEEXT_23) $(am__EXEEXT_24) $(am__EXEEXT_25) \ $(am__EXEEXT_26) $(am__EXEEXT_27) $(am__EXEEXT_28) \ - $(am__EXEEXT_29) $(am__EXEEXT_30) + $(am__EXEEXT_29) $(am__EXEEXT_30) $(am__EXEEXT_31) noinst_PROGRAMS = tangleboot$(EXEEXT) ctangleboot$(EXEEXT) \ - $(am__EXEEXT_31) $(am__EXEEXT_32) $(am__EXEEXT_33) \ - $(am__EXEEXT_34) $(am__EXEEXT_35) $(am__EXEEXT_36) \ - $(am__EXEEXT_37) $(am__EXEEXT_38) $(am__EXEEXT_39) \ - $(am__EXEEXT_40) $(am__EXEEXT_41) -TESTS = tangle.test $(am__EXEEXT_44) ctiedir/ctie.test \ + $(am__EXEEXT_32) $(am__EXEEXT_33) $(am__EXEEXT_34) \ + $(am__EXEEXT_35) $(am__EXEEXT_36) $(am__EXEEXT_37) \ + $(am__EXEEXT_38) $(am__EXEEXT_39) $(am__EXEEXT_40) \ + $(am__EXEEXT_41) $(am__EXEEXT_42) +TESTS = tangle.test $(am__EXEEXT_45) ctiedir/ctie.test \ cwebdir/ctwill.test cwebdir/cweave.test tiedir/tie.test \ $(am__append_6) $(am__append_16) $(am__append_25) \ $(am__append_34) $(am__append_42) $(am__append_55) \ $(am__append_56) $(am__append_58) $(am__append_62) \ - $(am__EXEEXT_45) $(am__append_68) $(am__append_71) \ + $(am__EXEEXT_46) $(am__append_68) $(am__append_71) \ $(am__append_76) $(am__append_81) $(am__append_84) \ $(am__append_105) $(am__append_106) $(am__append_107) \ - $(am__append_108) $(am__append_116) $(am__append_118) \ - $(am__append_120) $(am__append_143) libmd5/md5.test + $(am__append_108) $(am__append_117) $(am__append_119) \ + $(am__append_121) $(am__append_144) libmd5/md5.test @WEB_TRUE@am__append_1 = $(web_programs) @WEB_TRUE@am__append_2 = $(web_tests) @TEX_TRUE@am__append_3 = tex @@ -249,85 +249,86 @@ TESTS = tangle.test $(am__EXEEXT_44) ctiedir/ctie.test \ @LUAHBTEX_TRUE@am__append_106 = $(luahbtex_tests) @LUAJITTEX_TRUE@am__append_107 = $(luajittex_tests) @LUAJITHBTEX_TRUE@am__append_108 = $(luajithbtex_tests) -@XETEX_TRUE@am__append_109 = xetex -@XETEX_MACOSX_TRUE@am__append_110 = -DXETEX_MAC -@XETEX_MACOSX_TRUE@am__append_111 = -std=c++11 -@XETEX_MACOSX_FALSE@am__append_112 = $(FONTCONFIG_INCLUDES) -@XETEX_MACOSX_FALSE@am__append_113 = $(FONTCONFIG_LIBS) -@XETEX_MACOSX_TRUE@am__append_114 = \ +@TEXPROF_TRUE@am__append_109 = texprof texprofile +@XETEX_TRUE@am__append_110 = xetex +@XETEX_MACOSX_TRUE@am__append_111 = -DXETEX_MAC +@XETEX_MACOSX_TRUE@am__append_112 = -std=c++11 +@XETEX_MACOSX_FALSE@am__append_113 = $(FONTCONFIG_INCLUDES) +@XETEX_MACOSX_FALSE@am__append_114 = $(FONTCONFIG_LIBS) +@XETEX_MACOSX_TRUE@am__append_115 = \ @XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontInst_Mac.cpp \ @XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontInst_Mac.h \ @XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontMgr_Mac.mm \ @XETEX_MACOSX_TRUE@ xetexdir/XeTeXFontMgr_Mac.h \ @XETEX_MACOSX_TRUE@ xetexdir/XeTeX_mac.c -@XETEX_MACOSX_FALSE@am__append_115 = \ +@XETEX_MACOSX_FALSE@am__append_116 = \ @XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.cpp \ @XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.h -@XETEX_TRUE@am__append_116 = $(xetex_tests) -@OTANGLE_TRUE@am__append_117 = $(omegaware_programs) -@OTANGLE_TRUE@am__append_118 = $(OTANGLE_tests) $(OMFONTS_tests) -@ALEPH_TRUE@am__append_119 = aleph -@ALEPH_TRUE@am__append_120 = $(aleph_tests) -@SYNCTEX_TRUE@am__append_121 = synctex -@SYNCTEX_TRUE@am__append_122 = $(LTLIBSYNCTEX) -@SYNCTEX_TRUE@am__append_123 = $(LIBSYNCTEX) -@MINGW32_TRUE@am__append_124 = $(KPATHSEA_INCLUDES) -@MINGW32_TRUE@am__append_125 = -lshlwapi $(KPATHSEA_LIBS) -@MINGW32_TRUE@am__append_126 = -lshlwapi -@TEX_SYNCTEX_TRUE@am__append_127 = -I$(srcdir)/synctexdir \ +@XETEX_TRUE@am__append_117 = $(xetex_tests) +@OTANGLE_TRUE@am__append_118 = $(omegaware_programs) +@OTANGLE_TRUE@am__append_119 = $(OTANGLE_tests) $(OMFONTS_tests) +@ALEPH_TRUE@am__append_120 = aleph +@ALEPH_TRUE@am__append_121 = $(aleph_tests) +@SYNCTEX_TRUE@am__append_122 = synctex +@SYNCTEX_TRUE@am__append_123 = $(LTLIBSYNCTEX) +@SYNCTEX_TRUE@am__append_124 = $(LIBSYNCTEX) +@MINGW32_TRUE@am__append_125 = $(KPATHSEA_INCLUDES) +@MINGW32_TRUE@am__append_126 = -lshlwapi $(KPATHSEA_LIBS) +@MINGW32_TRUE@am__append_127 = -lshlwapi +@TEX_SYNCTEX_TRUE@am__append_128 = -I$(srcdir)/synctexdir \ @TEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @TEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-tex.h\" -@TEX_SYNCTEX_TRUE@am__append_128 = $(ZLIB_LIBS) -@TEX_SYNCTEX_TRUE@am__append_129 = $(ZLIB_DEPEND) -@TEX_SYNCTEX_TRUE@am__append_130 = \ +@TEX_SYNCTEX_TRUE@am__append_129 = $(ZLIB_LIBS) +@TEX_SYNCTEX_TRUE@am__append_130 = $(ZLIB_DEPEND) +@TEX_SYNCTEX_TRUE@am__append_131 = \ @TEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @TEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @TEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @TEX_SYNCTEX_TRUE@ synctexdir/synctex-tex.h -@ETEX_SYNCTEX_TRUE@am__append_131 = -I$(srcdir)/synctexdir \ +@ETEX_SYNCTEX_TRUE@am__append_132 = -I$(srcdir)/synctexdir \ @ETEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @ETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-etex.h\" -@ETEX_SYNCTEX_TRUE@am__append_132 = $(ZLIB_LIBS) -@ETEX_SYNCTEX_TRUE@am__append_133 = $(ZLIB_DEPEND) -@ETEX_SYNCTEX_TRUE@am__append_134 = \ +@ETEX_SYNCTEX_TRUE@am__append_133 = $(ZLIB_LIBS) +@ETEX_SYNCTEX_TRUE@am__append_134 = $(ZLIB_DEPEND) +@ETEX_SYNCTEX_TRUE@am__append_135 = \ @ETEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @ETEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @ETEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @ETEX_SYNCTEX_TRUE@ synctexdir/synctex-etex.h -@EUPTEX_SYNCTEX_TRUE@am__append_135 = -I$(srcdir)/synctexdir \ +@EUPTEX_SYNCTEX_TRUE@am__append_136 = -I$(srcdir)/synctexdir \ @EUPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @EUPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-euptex.h\" -@EUPTEX_SYNCTEX_TRUE@am__append_136 = $(ZLIB_LIBS) -@EUPTEX_SYNCTEX_TRUE@am__append_137 = $(ZLIB_DEPEND) -@EUPTEX_SYNCTEX_TRUE@am__append_138 = \ +@EUPTEX_SYNCTEX_TRUE@am__append_137 = $(ZLIB_LIBS) +@EUPTEX_SYNCTEX_TRUE@am__append_138 = $(ZLIB_DEPEND) +@EUPTEX_SYNCTEX_TRUE@am__append_139 = \ @EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @EUPTEX_SYNCTEX_TRUE@ synctexdir/synctex-euptex.h -@PDFTEX_SYNCTEX_TRUE@am__append_139 = -I$(srcdir)/synctexdir \ +@PDFTEX_SYNCTEX_TRUE@am__append_140 = -I$(srcdir)/synctexdir \ @PDFTEX_SYNCTEX_TRUE@ -D__SyncTeX__ \ @PDFTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-pdftex.h\" -@PDFTEX_SYNCTEX_TRUE@am__append_140 = \ +@PDFTEX_SYNCTEX_TRUE@am__append_141 = \ @PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @PDFTEX_SYNCTEX_TRUE@ synctexdir/synctex-pdftex.h -@XETEX_SYNCTEX_TRUE@am__append_141 = -I$(srcdir)/synctexdir \ +@XETEX_SYNCTEX_TRUE@am__append_142 = -I$(srcdir)/synctexdir \ @XETEX_SYNCTEX_TRUE@ -D__SyncTeX__ \ @XETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-xetex.h\" -@XETEX_SYNCTEX_TRUE@am__append_142 = \ +@XETEX_SYNCTEX_TRUE@am__append_143 = \ @XETEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @XETEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @XETEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @XETEX_SYNCTEX_TRUE@ synctexdir/synctex-xetex.h -@SYNCTEX_TRUE@am__append_143 = $(synctex_tests) +@SYNCTEX_TRUE@am__append_144 = $(synctex_tests) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/web2c-disable.m4 \ @@ -424,25 +425,26 @@ am__EXEEXT_3 = odvicopy$(EXEEXT) odvitype$(EXEEXT) otangle$(EXEEXT) \ @LUAHBTEX_TRUE@am__EXEEXT_24 = luahbtex$(EXEEXT) @LUAJITTEX_TRUE@am__EXEEXT_25 = luajittex$(EXEEXT) @LUAJITHBTEX_TRUE@am__EXEEXT_26 = luajithbtex$(EXEEXT) -@XETEX_TRUE@am__EXEEXT_27 = xetex$(EXEEXT) -@OTANGLE_TRUE@am__EXEEXT_28 = $(am__EXEEXT_3) -@ALEPH_TRUE@am__EXEEXT_29 = aleph$(EXEEXT) -@SYNCTEX_TRUE@am__EXEEXT_30 = synctex$(EXEEXT) +@TEXPROF_TRUE@am__EXEEXT_27 = texprof$(EXEEXT) texprofile$(EXEEXT) +@XETEX_TRUE@am__EXEEXT_28 = xetex$(EXEEXT) +@OTANGLE_TRUE@am__EXEEXT_29 = $(am__EXEEXT_3) +@ALEPH_TRUE@am__EXEEXT_30 = aleph$(EXEEXT) +@SYNCTEX_TRUE@am__EXEEXT_31 = synctex$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(libdir)" "$(DESTDIR)$(bindir)" \ "$(DESTDIR)$(man1dir)" "$(DESTDIR)$(man5dir)" \ "$(DESTDIR)$(pkgconfigdir)" "$(DESTDIR)$(syncincludedir)" -@MF_TRUE@@WIN32_TRUE@am__EXEEXT_31 = call_mf$(EXEEXT) -@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_32 = call_mflua$(EXEEXT) -@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_33 = call_mfluajit$(EXEEXT) -@MP_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mpost$(EXEEXT) -@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_pmpost$(EXEEXT) -@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_upmpost$(EXEEXT) -@HITEX_TRUE@am__EXEEXT_37 = himktables$(EXEEXT) -@LUATEX_TRUE@@WIN32_TRUE@am__EXEEXT_38 = call_luatex$(EXEEXT) -@LUAHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_39 = call_luahbtex$(EXEEXT) -@LUAJITTEX_TRUE@@WIN32_TRUE@am__EXEEXT_40 = call_luajittex$(EXEEXT) -@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_41 = \ +@MF_TRUE@@WIN32_TRUE@am__EXEEXT_32 = call_mf$(EXEEXT) +@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_33 = call_mflua$(EXEEXT) +@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mfluajit$(EXEEXT) +@MP_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_mpost$(EXEEXT) +@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_pmpost$(EXEEXT) +@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_37 = call_upmpost$(EXEEXT) +@HITEX_TRUE@am__EXEEXT_38 = himktables$(EXEEXT) +@LUATEX_TRUE@@WIN32_TRUE@am__EXEEXT_39 = call_luatex$(EXEEXT) +@LUAHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_40 = call_luahbtex$(EXEEXT) +@LUAJITTEX_TRUE@@WIN32_TRUE@am__EXEEXT_41 = call_luajittex$(EXEEXT) +@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_42 = \ @LUAJITHBTEX_TRUE@@WIN32_TRUE@ call_luajithbtex$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -1599,6 +1601,10 @@ tex_OBJECTS = $(dist_tex_OBJECTS) $(nodist_tex_OBJECTS) @TEX_SYNCTEX_TRUE@am__DEPENDENCIES_13 = $(am__DEPENDENCIES_1) tex_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_13) +texprof_SOURCES = texprof.c +texprof_OBJECTS = texprof-texprof.$(OBJEXT) +texprofile_SOURCES = texprofile.c +texprofile_OBJECTS = texprofile-texprofile.$(OBJEXT) nodist_tftopl_OBJECTS = tftopl.$(OBJEXT) tftopl_OBJECTS = $(nodist_tftopl_OBJECTS) tftopl_LDADD = $(LDADD) @@ -1803,7 +1809,8 @@ am__depfiles_remade = ./$(DEPDIR)/aleph-aleph-pool.Po \ ./$(DEPDIR)/ptex-ptexini.Po ./$(DEPDIR)/tangle.Po \ ./$(DEPDIR)/tangleboot.Po ./$(DEPDIR)/tex-tex-pool.Po \ ./$(DEPDIR)/tex-tex0.Po ./$(DEPDIR)/tex-texextra.Po \ - ./$(DEPDIR)/tex-texini.Po ./$(DEPDIR)/tftopl.Po \ + ./$(DEPDIR)/tex-texini.Po ./$(DEPDIR)/texprof-texprof.Po \ + ./$(DEPDIR)/texprofile-texprofile.Po ./$(DEPDIR)/tftopl.Po \ ./$(DEPDIR)/tie-tie.Po ./$(DEPDIR)/twill.Po \ ./$(DEPDIR)/upbibtex-upbibtex.Po \ ./$(DEPDIR)/updvitype-updvitype.Po ./$(DEPDIR)/upmpost-pmp.Po \ @@ -2516,8 +2523,8 @@ SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \ $(nodist_pmpost_SOURCES) $(nodist_pooltype_SOURCES) \ $(dist_ptex_SOURCES) $(nodist_ptex_SOURCES) $(synctex_SOURCES) \ $(nodist_tangle_SOURCES) $(nodist_tangleboot_SOURCES) \ - $(dist_tex_SOURCES) $(nodist_tex_SOURCES) \ - $(nodist_tftopl_SOURCES) $(nodist_tie_SOURCES) \ + $(dist_tex_SOURCES) $(nodist_tex_SOURCES) texprof.c \ + texprofile.c $(nodist_tftopl_SOURCES) $(nodist_tie_SOURCES) \ $(ttf2afm_SOURCES) $(EXTRA_ttf2afm_SOURCES) \ $(nodist_twill_SOURCES) $(upbibtex_SOURCES) \ $(nodist_upbibtex_SOURCES) $(updvitype_SOURCES) \ @@ -2546,10 +2553,11 @@ DIST_SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \ $(dist_mf_nowin_SOURCES) $(dist_mflua_SOURCES) \ $(dist_mfluajit_SOURCES) $(am__dist_pdftex_SOURCES_DIST) \ $(pdftosrc_SOURCES) $(dist_ptex_SOURCES) $(synctex_SOURCES) \ - $(am__dist_tex_SOURCES_DIST) $(ttf2afm_SOURCES) \ - $(EXTRA_ttf2afm_SOURCES) $(upbibtex_SOURCES) \ - $(updvitype_SOURCES) $(uppltotf_SOURCES) $(dist_uptex_SOURCES) \ - $(uptftopl_SOURCES) $(am__dist_xetex_SOURCES_DIST) + $(am__dist_tex_SOURCES_DIST) texprof.c texprofile.c \ + $(ttf2afm_SOURCES) $(EXTRA_ttf2afm_SOURCES) \ + $(upbibtex_SOURCES) $(updvitype_SOURCES) $(uppltotf_SOURCES) \ + $(dist_uptex_SOURCES) $(uptftopl_SOURCES) \ + $(am__dist_xetex_SOURCES_DIST) RECURSIVE_TARGETS = all-recursive check-recursive cscopelist-recursive \ ctags-recursive dvi-recursive html-recursive info-recursive \ install-data-recursive install-dvi-recursive \ @@ -2761,15 +2769,15 @@ am__set_TESTS_bases = \ bases=`echo $$bases` AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) -am__EXEEXT_42 = bibtex.test dvicopy.test dvitype.test gftodvi.test \ +am__EXEEXT_43 = 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 twill.test -am__EXEEXT_43 = $(am__EXEEXT_42) tests/bibtex-openout-test.pl \ +am__EXEEXT_44 = $(am__EXEEXT_43) tests/bibtex-openout-test.pl \ tests/bibtex-longline-test.pl tests/bibtex-mem.test \ tests/bibtex-bigauth.test tests/bibtex-auxinclude.test -@WEB_TRUE@am__EXEEXT_44 = $(am__EXEEXT_43) -am__EXEEXT_45 = +@WEB_TRUE@am__EXEEXT_45 = $(am__EXEEXT_44) +am__EXEEXT_46 = TEST_SUITE_LOG = test-suite.log am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) @@ -2822,8 +2830,9 @@ am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/../../am/bin_links.am \ $(srcdir)/pdftexdir/am/ttf2afm.am \ $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/ptexdir/am/ptex.am \ $(srcdir)/silent-sh.in $(srcdir)/synctexdir/am/synctex.am \ - $(srcdir)/tangle-sh.in $(srcdir)/uptexdir/am/uptex.am \ - $(srcdir)/web2c-sh.in $(srcdir)/xetexdir/am/xetex.am \ + $(srcdir)/tangle-sh.in $(srcdir)/texprofdir/am/texprof.am \ + $(srcdir)/uptexdir/am/uptex.am $(srcdir)/web2c-sh.in \ + $(srcdir)/xetexdir/am/xetex.am \ $(top_srcdir)/../../build-aux/ar-lib \ $(top_srcdir)/../../build-aux/compile \ $(top_srcdir)/../../build-aux/config.guess \ @@ -3002,7 +3011,7 @@ NM = @NM@ NMEDIT = @NMEDIT@ OBJCXX = @OBJCXX@ OBJCXXDEPMODE = @OBJCXXDEPMODE@ -OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_111) +OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_112) OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTANGLE = @OTANGLE@ @@ -3151,8 +3160,8 @@ EXTRA_LIBRARIES = libmf.a libmflua.a libmfluaotfcc.a libmfluapotrace.a \ libluaharfbuzz.a libluajitharfbuzz.a libxetex.a libsynctex.a \ libmd5.a EXTRA_LTLIBRARIES = libsynctex.la -lib_LIBRARIES = $(am__append_123) -lib_LTLIBRARIES = $(am__append_122) +lib_LIBRARIES = $(am__append_124) +lib_LTLIBRARIES = $(am__append_123) dist_man_MANS = synctexdir/man1/synctex.1 synctexdir/man5/synctex.5 nodist_man_MANS = TEST_EXTENSIONS = .pl .test @@ -3261,6 +3270,16 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \ ptexdir/tests/chcode-euc.pl ptexdir/tests/chcode-sjis.pl \ ptexdir/tests/skipjfmp.pl ptexdir/tests/skipjfmp.tfm \ ptexdir/ptrip/ptrip.diffs ptexdir/ptrip/texmf.cnf \ + ptexdir/tests/ctrlsym.tex ptexdir/tests/ctrlsym.log \ + ptexdir/tests/endlinechar.tex ptexdir/tests/endline.log \ + ptexdir/tests/control-symbol.tex ptexdir/tests/jctrlsym.log \ + ptexdir/tests/p-endlinea.typ ptexdir/tests/p-jctrlsyma.typ \ + ptexdir/tests/let_char_token.tex ptexdir/tests/chartoken.log \ + ptexdir/tests/ptexlineendmode.tex ptexdir/tests/p-lineenda.typ \ + ptexdir/tests/printkanji.tex ptexdir/tests/pknj-s.log \ + ptexdir/tests/pknj-e.log ptexdir/tests/p-pknj-sa.typ \ + ptexdir/tests/p-pknj-ea.typ ptexdir/tests/ucs.tex \ + ptexdir/tests/ucs-s.log ptexdir/tests/ucs-e.log \ $(uptex_web_srcs) $(uptex_ch_srcs) uptexdir/uptex.defines \ uptexdir/upbibtex.ch uptexdir/updvitype.ch \ uptexdir/uppltotf.ch uptexdir/uptftopl.ch uptexdir/COPYRIGHT \ @@ -3288,23 +3307,41 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \ euptexdir/pdfprimitive.test \ euptexdir/tests/pdfprimitive-test.tex \ euptexdir/tests/pdfprimitive-euptex.log \ - euptexdir/tests/ctrlsym.log euptexdir/tests/ctrlsym.tex \ - hitexdir/ChangeLog $(hitex_web) $(hitex_tests) \ - hitexdir/tests/hello.tex hitexdir/tests/rule.tex \ - pdftexdir/regex/COPYING.LIB pdftexdir/regex/README \ - $(pdftex_ch_srcs) pdftexdir/pdftex.defines pdftexdir/ChangeLog \ - pdftexdir/NEWS pdftexdir/README pdftexdir/change-files.txt \ - $(pdftex_tests) tests/wprob.tex pdftexdir/tests/pdfimage.tex \ - tests/1-4.jpg tests/B.pdf tests/basic.tex \ - tests/lily-ledger-broken.png tests/expanded.tex \ - tests/expanded.txt tests/cnfline.tex tests/partoken-ok.tex \ - tests/partoken-xfail.tex $(ttf2afm_tests) \ - pdftexdir/tests/postV3.afm pdftexdir/tests/postV3.ttf \ - pdftexdir/tests/postV7.afm pdftexdir/tests/postV7.ttf \ - $(pdftosrc_tests) pdftexdir/tests/test-13.pdf \ - pdftexdir/tests/test-13.xref pdftexdir/tests/test-15.pdf \ - pdftexdir/tests/test-15.xref $(libluasocket_sources) \ - luatexdir/luasocket/src/ftp_lua.c \ + euptexdir/tests/ctrlsym.tex euptexdir/tests/ctrlsym.log \ + euptexdir/tests/endlinechar.tex euptexdir/tests/endline.log \ + euptexdir/tests/control-symbol.tex \ + euptexdir/tests/jctrlsym.log euptexdir/tests/eup-endlinea.typ \ + euptexdir/tests/eup-jctrlsyma.typ \ + euptexdir/tests/printkanji.tex euptexdir/tests/pknj-s.log \ + euptexdir/tests/pknj-e.log euptexdir/tests/pknj-u.log \ + euptexdir/tests/eup-pknj-sa.typ \ + euptexdir/tests/eup-pknj-ea.typ \ + euptexdir/tests/eup-pknj-ua.typ euptexdir/tests/postpena.tex \ + euptexdir/tests/postpena.log euptexdir/tests/free_ixsp.tex \ + euptexdir/tests/free_ixsp.log euptexdir/tests/free_pena.tex \ + euptexdir/tests/free_pena.log euptexdir/tests/inhibitglue.tex \ + euptexdir/tests/eup-inhibitgluea.typ \ + euptexdir/tests/kinsoku-table.tex \ + euptexdir/tests/kinsoku-table.log \ + euptexdir/tests/kinsoku-search.tex \ + euptexdir/tests/kinsoku-search.log euptexdir/tests/ucs.tex \ + euptexdir/tests/ucs-s.log euptexdir/tests/ucs-e.log \ + euptexdir/tests/ucs-u.log hitexdir/ChangeLog $(hitex_web) \ + $(hitex_tests) hitexdir/tests/hello.tex \ + hitexdir/tests/rule.tex pdftexdir/regex/COPYING.LIB \ + pdftexdir/regex/README $(pdftex_ch_srcs) \ + pdftexdir/pdftex.defines pdftexdir/ChangeLog pdftexdir/NEWS \ + pdftexdir/README pdftexdir/change-files.txt $(pdftex_tests) \ + tests/wprob.tex pdftexdir/tests/pdfimage.tex tests/1-4.jpg \ + tests/B.pdf tests/basic.tex tests/lily-ledger-broken.png \ + tests/expanded.tex tests/expanded.txt tests/cnfline.tex \ + tests/partoken-ok.tex tests/partoken-xfail.tex \ + $(ttf2afm_tests) pdftexdir/tests/postV3.afm \ + pdftexdir/tests/postV3.ttf pdftexdir/tests/postV7.afm \ + pdftexdir/tests/postV7.ttf $(pdftosrc_tests) \ + pdftexdir/tests/test-13.pdf pdftexdir/tests/test-13.xref \ + pdftexdir/tests/test-15.pdf pdftexdir/tests/test-15.xref \ + $(libluasocket_sources) luatexdir/luasocket/src/ftp_lua.c \ luatexdir/luasocket/src/headers_lua.c \ luatexdir/luasocket/src/http_lua.c \ luatexdir/luasocket/src/ltn12_lua.c \ @@ -3484,9 +3521,10 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \ luatexdir/luaharfbuzz/luaharfbuzz-scm-1.rockspec \ luatexdir/luaharfbuzz/Makefile luatexdir/luaharfbuzz/README.md \ luatexdir/luaharfbuzz/README-win.md \ - luatexdir/luaharfbuzz/THANKS $(xetex_web_srcs) \ - $(xetex_ch_srcs) xetexdir/xetex.defines xetexdir/ChangeLog \ - xetexdir/COPYING xetexdir/NEWS xetexdir/image/README \ + luatexdir/luaharfbuzz/THANKS texprofdir/ChangeLog \ + $(texprof_web) $(xetex_web_srcs) $(xetex_ch_srcs) \ + xetexdir/xetex.defines xetexdir/ChangeLog xetexdir/COPYING \ + xetexdir/NEWS xetexdir/image/README \ xetexdir/unicode-char-prep.pl xetexdir/xewebmac.tex \ $(xetex_tests) xetexdir/tests/bug73.log \ xetexdir/tests/bug73.tex xetexdir/tests/filedump.log \ @@ -3599,7 +3637,9 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \ ptests/memtest.bib ptests/memtest?.* ptests/xstory.dvityp \ ptests/xpagenum.typ ptests/xptex[34]*.typ ptests/xcmr10.tfm \ ptests/xcmr10.pl ptests/xsample*.typ ptests/x*min10.* \ - ptests/xchcode*.* ptests/xskipjfmp.* ptrip.diffs \ + ptests/xchcode*.* ptests/xskipjfmp.* ptrip.diffs p-ctrlsym.log \ + p-ctrlsym.out p-ctrlsym.tex p-endline*.* p-jctrlsym*.* \ + p-chartoken.* p-lineend.* p-pknj-*.* p-ucs*.* \ $(nodist_uptex_SOURCES) uptex.web uptex.ch uptex-web2c uptex.p \ uptex.pool uptex-tangle $(upweb_programs:=.c) \ $(upweb_programs:=.h) $(upweb_programs:=.p) \ @@ -3614,40 +3654,42 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \ uptests/ygk256*.* uptests/xskipjfmp.* uptrip.diffs \ $(nodist_euptex_SOURCES) euptex.web euptex.ch euptex-web2c \ euptex.p euptex.pool euptex-tangle euptrip.diffs \ - pdfprimitive-euptex.* eup-ctrlsym.fmt eup-ctrlsym.log \ - eup-ctrlsym.out eup-ctrlsym.tex $(nodist_hitex_SOURCES) \ - $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \ - hiformat-tangle hitex-tangle hello.log rule.log \ - $(nodist_pdftex_SOURCES) pdftex-final.ch pdftex-web2c pdftex.p \ - pdftex.pool pdftex-tangle pwprob.log pwprob.tex pdfimage.fmt \ - pdfimage.log pdfimage.pdf expanded.log cnfline.log \ - partoken-ok.log partoken-xfail.log pdftests/fn*.* postV3.afm \ - postV7.afm test-13.pdf test-13.xref test-15.pdf test-15.xref \ - $(nodist_libluatex_sources) luaimage.* luajitimage.* \ - $(nodist_xetex_SOURCES) xetex.web xetex-final.ch xetex-web2c \ - xetex.p xetex.pool xetex-tangle bug73.fmt bug73.log bug73.out \ - bug73.tex filedump.log filedump.out filedump.tex \ - xe-ctrlsym.fmt xe-ctrlsym.log xe-ctrlsym.out xe-ctrlsym.tex \ - xetests/fn*.* $(omegaware_programs:=.c) \ - $(omegaware_programs:=.h) $(omegaware_programs:=.p) \ - $(omegaware_programs:=-web2c) ofm2opl.web opl2ofm.web \ - ovf2ovp.web ovp2ovf.web omegaware/bad*.* \ - omegaware/tests/charwdr.* omegaware/tests/charwdv.* \ - omegaware/tests/xcheck* omegaware/tests/xlevel1.* \ - omegaware/tests/xlig*.* ofont*vf omegaware/tests/xpagenum.* \ - omegaware/tests/xofont* omegaware/tests/Cherokee.tfm \ - omegaware/tests/OCherokee.ofm omegaware/tests/OCherokee.opl \ - omegaware/tests/OCherokee.ovf omegaware/tests/xCherokee.* \ - omegaware/tests/xOCherokee.* ocftest.* omegaware/tests/xinbmp* \ - omegaware/tests/xoverbmp* omegaware/tests/xrealnum.* \ - omegaware/tests/xrepeated.* omegaware/tests/sample*.ofm \ - omegaware/tests/sample*.ovf omegaware/tests/sample1-h.opl \ - omegaware/tests/xsample*.out omegaware/tests/shortend.* \ - omegaware/tests/specialhex.ofm omegaware/tests/specialhex.opl \ - omegaware/tests/specialhex.ovf omegaware/tests/xspecialhex.* \ - omegaware/tests/yrepeat* omegaware/tests/*yarabic* \ - $(nodist_aleph_SOURCES) aleph.web aleph.ch aleph-web2c aleph.p \ - aleph.pool aleph-tangle + pdfprimitive-euptex.* eup-ctrlsym.log eup-ctrlsym.out \ + eup-ctrlsym.tex eup-endline*.* eup-jctrlsym*.* eup-pknj-*.* \ + eup-postpena*.* eup-free_*.* eup-inhibitglue*.* eup-kinsoku*.* \ + eup-ucs*.* $(nodist_hitex_SOURCES) $(nodist_hishrink_SOURCES) \ + $(nodist_histretch_SOURCES) hiformat-tangle hitex-tangle \ + hello.log rule.log $(nodist_pdftex_SOURCES) pdftex-final.ch \ + pdftex-web2c pdftex.p pdftex.pool pdftex-tangle pwprob.log \ + pwprob.tex pdfimage.fmt pdfimage.log pdfimage.pdf expanded.log \ + cnfline.log partoken-ok.log partoken-xfail.log pdftests/fn*.* \ + postV3.afm postV7.afm test-13.pdf test-13.xref test-15.pdf \ + test-15.xref $(nodist_libluatex_sources) luaimage.* \ + luajitimage.* $(nodist_texprof_SOURCES) texprofile-tangle \ + texprof-tangle $(nodist_xetex_SOURCES) xetex.web \ + xetex-final.ch xetex-web2c xetex.p xetex.pool xetex-tangle \ + bug73.fmt bug73.log bug73.out bug73.tex filedump.log \ + filedump.out filedump.tex xe-ctrlsym.fmt xe-ctrlsym.log \ + xe-ctrlsym.out xe-ctrlsym.tex xetests/fn*.* \ + $(omegaware_programs:=.c) $(omegaware_programs:=.h) \ + $(omegaware_programs:=.p) $(omegaware_programs:=-web2c) \ + ofm2opl.web opl2ofm.web ovf2ovp.web ovp2ovf.web \ + omegaware/bad*.* omegaware/tests/charwdr.* \ + omegaware/tests/charwdv.* omegaware/tests/xcheck* \ + omegaware/tests/xlevel1.* omegaware/tests/xlig*.* ofont*vf \ + omegaware/tests/xpagenum.* omegaware/tests/xofont* \ + omegaware/tests/Cherokee.tfm omegaware/tests/OCherokee.ofm \ + omegaware/tests/OCherokee.opl omegaware/tests/OCherokee.ovf \ + omegaware/tests/xCherokee.* omegaware/tests/xOCherokee.* \ + ocftest.* omegaware/tests/xinbmp* omegaware/tests/xoverbmp* \ + omegaware/tests/xrealnum.* omegaware/tests/xrepeated.* \ + omegaware/tests/sample*.ofm omegaware/tests/sample*.ovf \ + omegaware/tests/sample1-h.opl omegaware/tests/xsample*.out \ + omegaware/tests/shortend.* omegaware/tests/specialhex.ofm \ + omegaware/tests/specialhex.opl omegaware/tests/specialhex.ovf \ + omegaware/tests/xspecialhex.* omegaware/tests/yrepeat* \ + omegaware/tests/*yarabic* $(nodist_aleph_SOURCES) aleph.web \ + aleph.ch aleph-web2c aleph.p aleph.pool aleph-tangle CLEANFILES = $(EXTRA_PROGRAMS) $(EXTRA_LIBRARIES) $(EXTRA_LTLIBRARIES) TRIPTRAP_CLEAN = $(am__append_8) $(am__append_18) $(am__append_27) \ $(am__append_36) $(am__append_44) $(am__append_60) \ @@ -3846,18 +3888,18 @@ tie_CPPFLAGS = $(AM_CPPFLAGS) -DNOT_WEB2C initex_CPPFLAGS = -DEXEPROG=\"tex.exe\" nodist_initex_SOURCES = callexe.c initex_LDADD = -tex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_127) +tex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_128) # With --enable-ipc, TeX may need to link with -lsocket. -tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_128) +tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_129) # TeX C sources tex_c_h = texini.c tex0.c texcoerce.h texd.h nodist_tex_SOURCES = $(tex_c_h) tex-pool.c -dist_tex_SOURCES = texextra.c $(am__append_130) +dist_tex_SOURCES = texextra.c $(am__append_131) # We must create texd.h before building the tex_OBJECTS. -tex_prereq = texd.h $(am__append_129) +tex_prereq = texd.h $(am__append_130) tex_ch_srcs = \ tex.web \ tex.ch \ @@ -4357,21 +4399,21 @@ libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w \ mplibdir/pngout.w mplibdir/mpmath.w mplibdir/mpmathbinary.w \ mplibdir/mpmathinterval.w mplibdir/mpmathdecimal.w \ mplibdir/mpmathdouble.w mplibdir/mpstrings.w mplibdir/tfmin.w -etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_131) +etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_132) # With --enable-ipc, e-TeX may need to link with -lsocket. -etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_132) +etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_133) etex_DEPENDENCIES = $(ZLIB_DEPEND) # e-TeX C sources etex_c_h = etexini.c etex0.c etexcoerce.h etexd.h nodist_etex_SOURCES = $(etex_c_h) etex-pool.c dist_etex_SOURCES = etexdir/etexextra.c etexdir/etexextra.h \ - etexdir/etex_version.h $(am__append_134) + etexdir/etex_version.h $(am__append_135) # We must create etexd.h and etexdir/etex_version.h before building the # etex_OBJECTS. -etex_prereq = etexd.h etexdir/etex_version.h $(am__append_133) +etex_prereq = etexd.h etexdir/etex_version.h $(am__append_134) etex_web_srcs = \ tex.web \ etexdir/etex.ch @@ -4436,7 +4478,10 @@ ptex_ch_srcs = \ ptex_tests = \ ptexdir/ptriptest.test \ ptexdir/pver.test \ - ptexdir/wcfname.test + ptexdir/wcfname.test \ + ptexdir/ptex-ctrlsym.test \ + ptexdir/ptex-prntknj.test \ + ptexdir/ptex-ucs.test uptex_cppflags = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) $(ZLIB_INCLUDES) uptex_ldadd = libukanji.a $(pproglib) $(PTEXENC_LIBS) $(LDADD) $(ZLIB_LIBS) @@ -4511,23 +4556,23 @@ upweb_tests = uptexdir/upbibtex.test uptexdir/updvitype.test \ uptexdir/newjfm.test uptexdir/uparse.test uptexdir/gkhuge.test \ uptexdir/gk256.test euptex_CPPFLAGS = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) \ - -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_135) + -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_136) # With --enable-ipc, e-upTeX may need to link with -lsocket. euptex_LDADD = libukanji.a $(pproglib) $(PTEXENC_LIBS) $(LDADD) \ - $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_136) + $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_137) euptex_DEPENDENCIES = libukanji.a $(pproglib) $(PTEXENC_DEPEND) $(default_dependencies) libmd5.a $(ZLIB_DEPEND) # e-upTeX C sources euptex_c_h = euptexini.c euptex0.c euptexcoerce.h euptexd.h nodist_euptex_SOURCES = $(euptex_c_h) euptex-pool.c dist_euptex_SOURCES = euptexdir/euptexextra.c euptexdir/euptexextra.h \ - $(am__append_138) + $(am__append_139) # We must create euptexd.h and [eu]ptexdir/[eu]ptex_version.h before building the euptex_OBJECTS. euptex_prereq = euptexd.h etexdir/etex_version.h \ uptexdir/ptex_version.h euptexdir/eptex_version.h \ - uptexdir/uptex_version.h $(am__append_137) + uptexdir/uptex_version.h $(am__append_138) euptex_web_srcs = \ tex.web \ etexdir/etex.ch \ @@ -4560,10 +4605,13 @@ euptex_ch_srcs = \ euptex_tests = \ euptexdir/euptriptest.test euptexdir/eptriptest.test \ euptexdir/pdfprimitive.test \ - euptexdir/euptex-ctrlsym.test \ euptexdir/eupver.test \ euptexdir/upkcat.test \ - euptexdir/wcfname.test euptexdir/wcfname0.test + euptexdir/wcfname.test euptexdir/wcfname0.test \ + euptexdir/euptex-ctrlsym.test \ + euptexdir/euptex-prntknj.test \ + euptexdir/euptex-kinsoku.test \ + euptexdir/euptex-ucs.test # At the moment, these are the only man pages handled in the @@ -4572,6 +4620,9 @@ euptex_tests = \ @HITEX_TRUE@man1_MANS = hitexdir/man/hishrink.man hitexdir/man/histretch.man \ @HITEX_TRUE@ hitexdir/man/hitex.man + +# See hitex.am for comments about handling man. +@TEXPROF_TRUE@man1_MANS = texprofdir/man/texprof.man texprofdir/man/texprofile.man hitex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) -I$(srcdir)/libmd5 \ -DINIT -DSTAT -DWEB2CVERSION="\"${WEB2CVERSION}\"" # -DDEBUG drops into an interactive debugging session. @@ -4660,7 +4711,7 @@ pdftex_dependencies = $(proglib) $(KPATHSEA_DEPEND) $(LIBPNG_DEPEND) \ # Force Automake to use CXXLD for linking nodist_EXTRA_pdftex_SOURCES = dummy.cxx pdf_tangle = WEBINPUTS=.:$(srcdir)/pdftexdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(TANGLE) -pdftex_CPPFLAGS = $(pdftex_cppflags) $(am__append_139) +pdftex_CPPFLAGS = $(pdftex_cppflags) $(am__append_140) pdftex_CXXFLAGS = $(WARNING_CXXFLAGS) # With --enable-ipc, pdfTeX may need to link with -lsocket. @@ -4673,7 +4724,7 @@ pdftex_c_h = pdftexini.c pdftex0.c pdftexcoerce.h pdftexd.h nodist_pdftex_SOURCES = $(pdftex_c_h) pdftex-pool.c dist_pdftex_SOURCES = pdftexdir/pdftexextra.c pdftexdir/pdftexextra.h \ pdftexdir/pdftex_version.h pdftexdir/etex_version.h \ - $(am__append_140) + $(am__append_141) pdftex_ch_srcs = \ pdftexdir/pdftex.web \ pdftexdir/tex.ch0 \ @@ -5348,6 +5399,26 @@ libluaharfbuzz_a_SOURCES = \ luatexdir/luaharfbuzz/src/luaharfbuzz/variation.c libluajitharfbuzz_a_SOURCES = $(libluaharfbuzz_a_SOURCES) +texprof_CPPFLAGS = $(AM_CPPFLAGS) -I$(srcdir)/libmd5 -DINIT -DSTAT \ + -DWEB2CVERSION="\"${WEB2CVERSION}\"" +# -DDEBUG drops into an interactive debugging session. +texprof_LDADD = $(KPATHSEA_LIBS) libmd5.a +texprof_DEPENDENCIES = $(KPATHSEA_DEPEND) libmd5.a +# +texprofile_CPPFLAGS = $(AM_CPPFLAGS) +texprofile_LDADD = +texprofile_DEPENDENCIES = +# +# TeXprof CWEB sources +texprof_web = texprofdir/texprof.w +texprofile_web = texprofdir/texprofile.w + +# Creating several files: need stamp file and two rules with identical recipes +texprof_ctangle_sh = CWEBINPUTS=$(srcdir)/texprofdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE) + +# For each cweb (.w) source file, list the files generated. +texprof_c = texprof.c +texprofile_c = texprofile.c # Force Automake to use CXXLD for linking nodist_EXTRA_xetex_SOURCES = dummy.cxx @@ -5356,18 +5427,18 @@ libxetex = libxetex.a xetex_cppflags = $(AM_CPPFLAGS) -I$(srcdir)/xetexdir $(ICU_INCLUDES) \ $(FREETYPE2_INCLUDES) $(TECKIT_INCLUDES) $(HARFBUZZ_INCLUDES) \ $(GRAPHITE2_INCLUDES) $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) \ - $(PPLIB_INCLUDES) -I$(srcdir)/libmd5 $(am__append_110) \ - $(am__append_112) + $(PPLIB_INCLUDES) -I$(srcdir)/libmd5 $(am__append_111) \ + $(am__append_113) xetex_ldadd = $(libxetex) $(HARFBUZZ_LIBS) $(GRAPHITE2_LIBS) \ $(ICU_LIBS) $(ICU_LIBS_EXTRA) $(TECKIT_LIBS) $(LIBPNG_LIBS) \ $(FREETYPE2_LIBS) $(PPLIB_LIBS) $(ZLIB_LIBS) libmd5.a \ - $(am__append_113) + $(am__append_114) xetex_dependencies = $(proglib) $(KPATHSEA_DEPEND) $(ICU_DEPEND) \ $(TECKIT_DEPEND) $(HARFBUZZ_DEPEND) $(GRAPHITE2_DEPEND) \ $(LIBPNG_DEPEND) $(FREETYPE2_DEPEND) $(ZLIB_DEPEND) \ $(PPLIB_DEPEND) libmd5.a @XETEX_MACOSX_TRUE@xetex_LDFLAGS = -framework ApplicationServices -framework Cocoa -xetex_CPPFLAGS = $(xetex_cppflags) $(am__append_141) +xetex_CPPFLAGS = $(xetex_cppflags) $(am__append_142) xetex_CFLAGS = $(WARNING_CFLAGS) xetex_CXXFLAGS = # $(WARNING_CXXFLAGS) xetex_LDADD = $(xetex_ldadd) $(LDADD) $(ipc_socketlibs) @@ -5376,7 +5447,7 @@ xetex_c_h = xetexini.c xetex0.c xetexcoerce.h xetexd.h nodist_xetex_SOURCES = $(xetex_c_h) xetex-pool.c dist_xetex_SOURCES = xetexdir/xetexextra.c xetexdir/xetexextra.h \ xetexdir/etex_version.h xetexdir/xetex_version.h \ - $(am__append_142) + $(am__append_143) xetex_ch_srcs = \ xetexdir/xetex.web \ xetexdir/tex.ch0 \ @@ -5410,7 +5481,7 @@ libxetex_a_SOURCES = xetexdir/XeTeXFontInst.cpp \ xetexdir/image/jpegimage.h xetexdir/image/mfileio.c \ xetexdir/image/mfileio.h xetexdir/image/numbers.c \ xetexdir/image/numbers.h xetexdir/image/pngimage.c \ - xetexdir/image/pngimage.h $(am__append_114) $(am__append_115) + xetexdir/image/pngimage.h $(am__append_115) $(am__append_116) # We must create xetexd.h etc. before building the libxetex_a_OBJECTS. libxetex_prereq = xetexd.h $(xetex_dependencies) @@ -5517,13 +5588,13 @@ aleph_tests = alephdir/aleph.test synctex_SOURCES = \ synctexdir/synctex_main.c -synctex_CPPFLAGS = -I$(srcdir)/synctexdir $(am__append_124) -synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_125) +synctex_CPPFLAGS = -I$(srcdir)/synctexdir $(am__append_125) +synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_126) libsynctex = $(LTLIBSYNCTEX) $(LIBSYNCTEX) libsynctex_la_CPPFLAGS = -I$(srcdir)/synctexdir $(ZLIB_INCLUDES) -DSYNCTEX_USE_LOCAL_HEADER libsynctex_a_CPPFLAGS = $(libsynctex_la_CPPFLAGS) libsynctex_la_LDFLAGS = -rpath @libdir@ -bindir @bindir@ -no-undefined -version-info $(SYNCTEX_LT_VERSINFO) -libsynctex_la_LIBADD = $(ZLIB_LIBS) $(am__append_126) +libsynctex_la_LIBADD = $(ZLIB_LIBS) $(am__append_127) libsynctex_la_SOURCES = \ synctexdir/synctex_parser.c \ synctexdir/synctex_parser_local.h \ @@ -5605,7 +5676,7 @@ all: ff-config.h .SUFFIXES: .c .cc .cin .cpp .cxx .h .hin .lo .log .mm .o .obj .p .pin .pl .pl$(EXEEXT) .test .test$(EXEEXT) .trs am--refresh: Makefile @: -$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.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 $(srcdir)/../../am/bin_links.am $(am__configure_deps) +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.am $(srcdir)/texprofdir/am/texprof.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 $(srcdir)/../../am/bin_links.am $(am__configure_deps) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -5627,7 +5698,7 @@ Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status echo ' cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles)'; \ cd $(top_builddir) && $(SHELL) ./config.status $@ $(am__maybe_remake_depfiles);; \ esac; -$(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.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 $(srcdir)/../../am/bin_links.am $(am__empty): +$(srcdir)/../../am/dist_hook.am $(srcdir)/am/bootstrap.am $(srcdir)/am/web.am $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/mfluadir/am/mflua.am $(srcdir)/mfluadir/am/mfluaotfcc.am $(srcdir)/mfluadir/am/mfluapotrace.am $(srcdir)/mfluadir/am/mflualpeg.am $(srcdir)/mfluajitdir/am/mfluajit.am $(srcdir)/mfluajitdir/am/mfluajitlpeg.am $(srcdir)/mplibdir/am/mplib.am $(srcdir)/pmpostdir/am/pmpost.am $(srcdir)/mplibdir/am/libmputil.am $(srcdir)/mplibdir/am/libmplib.am $(srcdir)/etexdir/am/etex.am $(srcdir)/ptexdir/am/ptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.am $(srcdir)/hitexdir/am/hitex.am $(srcdir)/pdftexdir/am/libpdftex.am $(srcdir)/pdftexdir/am/pdftex.am $(srcdir)/pdftexdir/am/ttf2afm.am $(srcdir)/pdftexdir/am/pdftosrc.am $(srcdir)/luatexdir/am/luasocket.am $(srcdir)/luatexdir/am/luamisc.am $(srcdir)/luatexdir/am/libunilib.am $(srcdir)/luatexdir/am/luafontforge.am $(srcdir)/luatexdir/am/libluatex.am $(srcdir)/luatexdir/am/luaffi.am $(srcdir)/luatexdir/am/luatex.am $(srcdir)/luatexdir/am/luaharfbuzz.am $(srcdir)/texprofdir/am/texprof.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 $(srcdir)/../../am/bin_links.am $(am__empty): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck @@ -8239,6 +8310,14 @@ tex$(EXEEXT): $(tex_OBJECTS) $(tex_DEPENDENCIES) $(EXTRA_tex_DEPENDENCIES) @rm -f tex$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tex_OBJECTS) $(tex_LDADD) $(LIBS) +texprof$(EXEEXT): $(texprof_OBJECTS) $(texprof_DEPENDENCIES) $(EXTRA_texprof_DEPENDENCIES) + @rm -f texprof$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(texprof_OBJECTS) $(texprof_LDADD) $(LIBS) + +texprofile$(EXEEXT): $(texprofile_OBJECTS) $(texprofile_DEPENDENCIES) $(EXTRA_texprofile_DEPENDENCIES) + @rm -f texprofile$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(texprofile_OBJECTS) $(texprofile_LDADD) $(LIBS) + tftopl$(EXEEXT): $(tftopl_OBJECTS) $(tftopl_DEPENDENCIES) $(EXTRA_tftopl_DEPENDENCIES) @rm -f tftopl$(EXEEXT) $(AM_V_CCLD)$(LINK) $(tftopl_OBJECTS) $(tftopl_LDADD) $(LIBS) @@ -8548,6 +8627,8 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tex-tex0.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tex-texextra.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tex-texini.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/texprof-texprof.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/texprofile-texprofile.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tftopl.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tie-tie.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/twill.Po@am__quote@ # am--include-marker @@ -18162,6 +18243,34 @@ tex-tex-pool.obj: tex-pool.c @AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o tex-tex-pool.obj `if test -f 'tex-pool.c'; then $(CYGPATH_W) 'tex-pool.c'; else $(CYGPATH_W) '$(srcdir)/tex-pool.c'; fi` +texprof-texprof.o: texprof.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprof-texprof.o -MD -MP -MF $(DEPDIR)/texprof-texprof.Tpo -c -o texprof-texprof.o `test -f 'texprof.c' || echo '$(srcdir)/'`texprof.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprof-texprof.Tpo $(DEPDIR)/texprof-texprof.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprof.c' object='texprof-texprof.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprof-texprof.o `test -f 'texprof.c' || echo '$(srcdir)/'`texprof.c + +texprof-texprof.obj: texprof.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprof-texprof.obj -MD -MP -MF $(DEPDIR)/texprof-texprof.Tpo -c -o texprof-texprof.obj `if test -f 'texprof.c'; then $(CYGPATH_W) 'texprof.c'; else $(CYGPATH_W) '$(srcdir)/texprof.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprof-texprof.Tpo $(DEPDIR)/texprof-texprof.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprof.c' object='texprof-texprof.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprof_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprof-texprof.obj `if test -f 'texprof.c'; then $(CYGPATH_W) 'texprof.c'; else $(CYGPATH_W) '$(srcdir)/texprof.c'; fi` + +texprofile-texprofile.o: texprofile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprofile-texprofile.o -MD -MP -MF $(DEPDIR)/texprofile-texprofile.Tpo -c -o texprofile-texprofile.o `test -f 'texprofile.c' || echo '$(srcdir)/'`texprofile.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprofile-texprofile.Tpo $(DEPDIR)/texprofile-texprofile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprofile.c' object='texprofile-texprofile.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprofile-texprofile.o `test -f 'texprofile.c' || echo '$(srcdir)/'`texprofile.c + +texprofile-texprofile.obj: texprofile.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT texprofile-texprofile.obj -MD -MP -MF $(DEPDIR)/texprofile-texprofile.Tpo -c -o texprofile-texprofile.obj `if test -f 'texprofile.c'; then $(CYGPATH_W) 'texprofile.c'; else $(CYGPATH_W) '$(srcdir)/texprofile.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/texprofile-texprofile.Tpo $(DEPDIR)/texprofile-texprofile.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='texprofile.c' object='texprofile-texprofile.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ +@am__fastdepCC_FALSE@ $(AM_V_CC@am__nodep@)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(texprofile_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o texprofile-texprofile.obj `if test -f 'texprofile.c'; then $(CYGPATH_W) 'texprofile.c'; else $(CYGPATH_W) '$(srcdir)/texprofile.c'; fi` + tie-tie.o: tie.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(tie_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT tie-tie.o -MD -MP -MF $(DEPDIR)/tie-tie.Tpo -c -o tie-tie.o `test -f 'tie.c' || echo '$(srcdir)/'`tie.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/tie-tie.Tpo $(DEPDIR)/tie-tie.Po @@ -19838,6 +19947,8 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/tex-tex0.Po -rm -f ./$(DEPDIR)/tex-texextra.Po -rm -f ./$(DEPDIR)/tex-texini.Po + -rm -f ./$(DEPDIR)/texprof-texprof.Po + -rm -f ./$(DEPDIR)/texprofile-texprofile.Po -rm -f ./$(DEPDIR)/tftopl.Po -rm -f ./$(DEPDIR)/tie-tie.Po -rm -f ./$(DEPDIR)/twill.Po @@ -20614,6 +20725,8 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/tex-tex0.Po -rm -f ./$(DEPDIR)/tex-texextra.Po -rm -f ./$(DEPDIR)/tex-texini.Po + -rm -f ./$(DEPDIR)/texprof-texprof.Po + -rm -f ./$(DEPDIR)/texprofile-texprofile.Po -rm -f ./$(DEPDIR)/tftopl.Po -rm -f ./$(DEPDIR)/tie-tie.Po -rm -f ./$(DEPDIR)/twill.Po @@ -21928,7 +22041,10 @@ ptex.web: tie$(EXEEXT) $(ptex_web_srcs) ptex.ch: tie$(EXEEXT) ptex.web $(ptex_ch_srcs) $(tie_c) ptex.web $(ptex_ch_srcs) ptexdir/ptriptest.log: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) -ptexdir/pver.log ptexdir/wcfname.log: ptex$(EXEEXT) +ptexdir/pver.log ptexdir/wcfname.log \ + ptexdir/ptex-ctrlsym.log ptexdir/ptex-prntknj.log \ + ptexdir/ptex-ucs.log: ptex$(EXEEXT) + ptrip.diffs: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) $(triptrap_diffs) $@ .PHONY: ptrip-clean @@ -22011,6 +22127,7 @@ uptexdir/sample.log: updvitype$(EXEEXT) uptexdir/yokotate.log uptexdir/skipjfmp.log \ uptexdir/newjfm.log uptexdir/uparse.log \ uptexdir/gkhuge.log uptexdir/gk256.log: uppltotf$(EXEEXT) uptftopl$(EXEEXT) + uptrip.diffs: uptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) $(triptrap_diffs) $@ .PHONY: uptrip-clean @@ -22044,9 +22161,12 @@ euptex.ch: tie$(EXEEXT) euptex.web $(euptex_ch_srcs) euptexdir/euptriptest.log euptexdir/eptriptest.log: \ euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) euptexdir/pdfprimitive.log: euptex$(EXEEXT) -euptexdir/euptex-ctrlsym.log \ - euptexdir/eupver.log euptexdir/upkcat.log \ - euptexdir/wcfname.log euptexdir/wcfname0.log: euptex$(EXEEXT) +euptexdir/eupver.log euptexdir/upkcat.log \ + euptexdir/wcfname.log euptexdir/wcfname0.log \ + euptexdir/euptex-ctrlsym.log euptexdir/euptex-prntknj.log \ + euptexdir/euptex-kinsoku.log \ + euptexdir/euptex-ucs.log: euptex$(EXEEXT) + euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) $(triptrap_diffs) $@ .PHONY: euptrip-clean @@ -22240,6 +22360,40 @@ luatexdir/luajithbtex.log luatexdir/luajithbimage.log: luajithbtex$(EXEEXT) $(libluaharfbuzz_a_OBJECTS): $(LUA_DEPEND) $(libluajitharfbuzz_a_OBJECTS): $(LUAJIT_DEPEND) + +# convenience target to build all the programs here. +@TEXPROF_TRUE@texprofall: texprof texprofile + +$(texprof_c): texprof-tangle +$(texprofile_c): texprofile-tangle + +# +texprof-tangle: ctangle$(EXEEXT) texprofdir/texprof.w tangle-sh + $(texprof_ctangle_sh) texprof + +texprofile-tangle: ctangle$(EXEEXT) texprofdir/texprofile.w tangle-sh + $(texprof_ctangle_sh) texprofile + +# +#texprof_tests = texprofdir/tests/hello.test \ +# texprofdir/tests/rule.test +# +#texprofdir/tests/hello.log: texprof$(EXEEXT) histretch$(EXEEXT) +#texprofdir/tests/rule.log: texprof$(EXEEXT) histretch$(EXEEXT) hishrink$(EXEEXT) +# +#EXTRA_DIST += $(texprof_tests) +# +#if TEXPROF +#TESTS += $(texprof_tests) +#endif TEXPROF +# +#EXTRA_DIST += texprofdir/tests/hello.tex +#DISTCLEANFILES += hello.log + +#EXTRA_DIST += texprofdir/tests/rule.tex +#DISTCLEANFILES += rule.log + +# (end of texprof.am) $(xetex_OBJECTS): $(xetex_prereq) $(xetex_c_h): xetex-web2c diff --git a/Build/source/texk/web2c/ac/web2c.ac b/Build/source/texk/web2c/ac/web2c.ac index 9901c93c40c..471c3250f66 100644 --- a/Build/source/texk/web2c/ac/web2c.ac +++ b/Build/source/texk/web2c/ac/web2c.ac @@ -43,6 +43,7 @@ m4_define([kpse_tex_progs], [dnl [[luajithbtex], [yes], [], [LuaHBJITTeX], [pplib libpng zziplib luajit harfbuzz]], [[mp], [yes], [], [MetaPost], [mpfi mpfr cairo libpng]], [[pmp], [yes], [], [pMetaPost], [mpfi mpfr cairo libpng ptexenc]], +[[texprof], [yes], [], [TeXprof], []], [[upmp], [yes], [], [upMetaPost], [mpfi mpfr cairo libpng ptexenc]], [[xetex], [yes], [yes], [XeTeX], [pplib libpng freetype2 teckit harfbuzz]], ])[]dnl diff --git a/Build/source/texk/web2c/bibtex.ch b/Build/source/texk/web2c/bibtex.ch index c8d566ee60b..b9661c65cdd 100644 --- a/Build/source/texk/web2c/bibtex.ch +++ b/Build/source/texk/web2c/bibtex.ch @@ -13,6 +13,12 @@ % 03/09/90 `int' is a bad variable name for C. % (more recent changes in the ChangeLog) +@x [0] +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + % [0] Let bibtex.tex work with latest webmac (which defines \ET, hence % making E active loses). @x @@ -104,7 +110,7 @@ begin initialize; print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> begin standard_input := stdin; standard_output := stdout; @@ -1633,7 +1639,7 @@ itself will get a new section number. @y @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 4; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure index 7f9c194d1d1..1b30570db8f 100755 --- a/Build/source/texk/web2c/configure +++ b/Build/source/texk/web2c/configure @@ -800,6 +800,8 @@ XETEX_FALSE XETEX_TRUE UPMP_FALSE UPMP_TRUE +TEXPROF_FALSE +TEXPROF_TRUE PMP_FALSE PMP_TRUE MP_FALSE @@ -1047,6 +1049,7 @@ enable_luahbtex enable_luajithbtex enable_mp enable_pmp +enable_texprof enable_upmp enable_xetex enable_xetex_synctex @@ -1792,6 +1795,7 @@ Optional Features: --disable-luajithbtex do not compile and install LuaHBJITTeX --disable-mp do not compile and install MetaPost --disable-pmp do not compile and install pMetaPost + --disable-texprof do not compile and install TeXprof --disable-upmp do not compile and install upMetaPost --disable-xetex do not compile and install XeTeX --disable-xetex-synctex build XeTeX without SyncTeX support @@ -20811,6 +20815,17 @@ test "x$enable_web2c:$enable_pmp" = xyes:yes && { need_libpng=yes need_ptexenc=yes } +# Check whether --enable-texprof was given. +if test ${enable_texprof+y} +then : + enableval=$enable_texprof; +fi +case $enable_texprof in #( + yes | no) : + ;; #( + *) : + enable_texprof=yes ;; +esac # Check whether --enable-upmp was given. if test ${enable_upmp+y} then : @@ -23601,6 +23616,13 @@ else PMP_TRUE='#' PMP_FALSE= fi + if test "x$enable_texprof" = xyes; then + TEXPROF_TRUE= + TEXPROF_FALSE='#' +else + TEXPROF_TRUE='#' + TEXPROF_FALSE= +fi if test "x$enable_upmp" = xyes; then UPMP_TRUE= UPMP_FALSE='#' @@ -29657,6 +29679,10 @@ if test -z "${PMP_TRUE}" && test -z "${PMP_FALSE}"; then as_fn_error $? "conditional \"PMP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${TEXPROF_TRUE}" && test -z "${TEXPROF_FALSE}"; then + as_fn_error $? "conditional \"TEXPROF\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${UPMP_TRUE}" && test -z "${UPMP_FALSE}"; then as_fn_error $? "conditional \"UPMP\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/Build/source/texk/web2c/ctangleboot.cin b/Build/source/texk/web2c/ctangleboot.cin index eefec8569c5..3483628ef0c 100644 --- a/Build/source/texk/web2c/ctangleboot.cin +++ b/Build/source/texk/web2c/ctangleboot.cin @@ -28,7 +28,7 @@ /*:5*/ #line 67 "cwebdir/ctangle.w" -#define banner "This is CTANGLE, Version 4.11" \ +#define banner "This is CTANGLE, Version 4.12" \ \ #define _(s) gettext(s) \ diff --git a/Build/source/texk/web2c/cwebdir/ChangeLog b/Build/source/texk/web2c/cwebdir/ChangeLog index 3a23a67014f..ecd275ac321 100644 --- a/Build/source/texk/web2c/cwebdir/ChangeLog +++ b/Build/source/texk/web2c/cwebdir/ChangeLog @@ -1,3 +1,72 @@ +2024-09-14 Andreas Scherer <https://ascherer.github.io> + + * ctwill-mini.c, + * ctwill-w2c.ch, + * cweav-w2c.ch, + * cweave.w, + * cwebacromac.tex, + * cwebmac.tex, + * refsort.ch: Fix sorting order in Names-of-the-Sections. + +2024-08-12 Andreas Scherer <https://ascherer.github.io> + + * Makefile + * comm-{mac,mini,ql,w2c}.ch + * comm-w2c.h + * common.h + * common.w + * ctang-{bs,pc,ql,vms,w2c,w32}.ch + * ctangle.c + * ctangle.w + * ctwill-w2c.ch + * cweav-{bs,pc,ql,vms,w2c,w32}.ch + * cweave.w + * cwebman-w2c.ch + * cwebman.tex + * po/cweb-tl.pot + * po/cweb.pot + * po/de/cweb-tl.po + * po/de/cweb.po + * po/it/cweb.po + * prod-{cweave,twill}.w + * prod.w: CWEB 4.12 release. + +2024-08-10 Andreas Scherer <https://ascherer.github.io> + + * cwebacromac.tex, + * cwebmac.tex, + * texinputs/icwebmac.tex, + * texinputs/pdfctproofmac.tex, + * texinputs/pdfctwimac.tex: Apply new scanning macros. + +2024-08-08 Andreas Scherer <https://ascherer.github.io> + + * comm-mini.ch, + * comm-w2c.ch: Demote 'Extensions' and subsections. + +2024-08-07 Andreas Scherer <https://ascherer.github.io> + + * comm-mini.ch, + * comm-w2c.ch: The Index is not an Extension. + +2024-08-06 Andreas Scherer <https://ascherer.github.io> + + * cwebacromac.tex, + * cwebmac.tex, + * cwebman.tex, + * texinputs/pdfctproofmac.tex, + * texinputs/pdfctwimac.tex: Refine PDF-related macros. + +2024-08-02 Andreas Scherer <https://ascherer.github.io> + + * ctwill-w2c.ch, + * cweav-w2c.ch, + * cweave.w: Remove unused 'kludge'. + +2024-07-26 Andreas Scherer <https://ascherer.github.io> + + * cwebmac.tex: Reshuffle '\note' macro. + 2024-07-24 Andreas Scherer <https://ascherer.github.io> * cwebmac.tex, diff --git a/Build/source/texk/web2c/cwebdir/Makefile b/Build/source/texk/web2c/cwebdir/Makefile index 28ea97d5540..a11936f9894 100644 --- a/Build/source/texk/web2c/cwebdir/Makefile +++ b/Build/source/texk/web2c/cwebdir/Makefile @@ -1,6 +1,6 @@ # This file is part of CWEB. # It is distributed WITHOUT ANY WARRANTY, express or implied. -# Version 4.11 --- December 2023 +# Version 4.12 --- August 2024 # Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth @@ -216,6 +216,6 @@ tarball: tar zcvhf /tmp/cweb.tgz $(ALL) examples ctan: - git archive -o ~/cweb-4.11.zip --prefix=cweb/ cweb-4.11 + git archive -o ~/cweb-4.12.zip --prefix=cweb/ cweb-4.12 make PDF=pdf usermanual - cd ..; zip -r ~/cweb-4.11.zip cweb/cwebman.pdf + cd ..; zip -r ~/cweb-4.12.zip cweb/cwebman.pdf diff --git a/Build/source/texk/web2c/cwebdir/comm-mac.ch b/Build/source/texk/web2c/cwebdir/comm-mac.ch index 79b1ec945e8..1540a9a4e21 100644 --- a/Build/source/texk/web2c/cwebdir/comm-mac.ch +++ b/Build/source/texk/web2c/cwebdir/comm-mac.ch @@ -4,9 +4,9 @@ No changes to CTANGLE or CWEAVE are needed. (Contributed 13 Oct 2000 by AndPio@aol.com; slightly edited by Don Knuth) @x in limbo, change the title page document to specify Mac version - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} @y - \centerline{(Version 4.11 for MacOS)} + \centerline{(Version 4.12 for MacOS)} @z @x section 23: Make input_ln accept \n, \r, \n\r, or \r\n as line endings diff --git a/Build/source/texk/web2c/cwebdir/comm-mini.ch b/Build/source/texk/web2c/cwebdir/comm-mini.ch index d1bd623b2a2..adf9fc0c28e 100644 --- a/Build/source/texk/web2c/cwebdir/comm-mini.ch +++ b/Build/source/texk/web2c/cwebdir/comm-mini.ch @@ -1,9 +1,9 @@ Limbo. @x -\def\title{Common code for CTANGLE and CWEAVE (4.11 [\TeX~Live])} +\def\title{Common code for CTANGLE and CWEAVE (4.12 [\TeX~Live])} @y -\def\title{COMMON (4.11 [\TeX~Live])} +\def\title{COMMON (4.12 [\TeX~Live])} \def\contentspagenumber{0} @z diff --git a/Build/source/texk/web2c/cwebdir/comm-ql.ch b/Build/source/texk/web2c/cwebdir/comm-ql.ch index 44cc39ec45f..8510fc093a2 100644 --- a/Build/source/texk/web2c/cwebdir/comm-ql.ch +++ b/Build/source/texk/web2c/cwebdir/comm-ql.ch @@ -10,24 +10,24 @@ ex <dev_>cc;'-v -h -c -=500000 -DCWEBINPUTS=flp2_ common_c' @x \def\v{\char'174} % vertical (|) in typewriter font -\def\title{Common code for CTANGLE and CWEAVE (Version 4.11)} +\def\title{Common code for CTANGLE and CWEAVE (Version 4.12)} \def\topofcontents{\null\vfill \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and {\ttitlefont CWEAVE}} \vskip 15pt - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} \vfill} \def\botofcontents{\vfill \noindent @y \def\v{\char'174} % vertical (|) in typewriter font -\def\title{Common code for CTANGLE and CWEAVE (QL Version 4.11)} +\def\title{Common code for CTANGLE and CWEAVE (QL Version 4.12)} \def\topofcontents{\null\vfill \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and {\ttitlefont CWEAVE}} \vskip 15pt - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} \vfill} \def\botofcontents{\vfill \noindent diff --git a/Build/source/texk/web2c/cwebdir/comm-w2c.ch b/Build/source/texk/web2c/cwebdir/comm-w2c.ch index 4035a633ffb..f0d5b6a8034 100644 --- a/Build/source/texk/web2c/cwebdir/comm-w2c.ch +++ b/Build/source/texk/web2c/cwebdir/comm-w2c.ch @@ -17,16 +17,16 @@ @q Please send comments, suggestions, etc. to tex-k@@tug.org. @> @x [0.0] l.25 -\def\title{Common code for CTANGLE and CWEAVE (Version 4.11)} +\def\title{Common code for CTANGLE and CWEAVE (Version 4.12)} @y \def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} \ifacro\sanitizecommand\Kpathsea{KPATHSEA}\fi -\def\title{Common code for CTANGLE and CWEAVE (4.11 [\TeX~Live])} +\def\title{Common code for CTANGLE and CWEAVE (4.12 [\TeX~Live])} @z @x [0.0] l.30 - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} @y - \centerline{(Version 4.11 [\TeX~Live])} + \centerline{(Version 4.12 [\TeX~Live])} @z @x [0.0] l.32 @@ -616,21 +616,21 @@ else { @x [9.85] l.1286 @* Index. @y -@** Extensions to {\tentex CWEB}. The following sections introduce new or +@* Extensions to {\tentex CWEB}. The following sections introduce new or improved features that have been created by numerous contributors over the course of a quarter century. Care has been taken to keep the original section numbering intact, so this new material should nicely integrate with the original ``\&{85.~Index}.'' -@* Language setting. This global variable is set by the argument of the +@*1 Language setting. This global variable is set by the argument of the `\.{+l}' (or `\.{-l}') command-line option. @<Global var...@>= const char *use_language=""; /* prefix of \.{cwebmac.tex} in \TEX/ output */ -@* User communication. The |scan_args| and |cb_show_banner| routines and the +@*1 User communication. The |scan_args| and |cb_show_banner| routines and the |bindtextdomain| argument string need a few extra variables. @d max_banner 50 @@ -647,7 +647,7 @@ string texmf_locale;@/ #endif char separators[]=SEPARATORS; -@* Temporary file output. Most \CEE/ projects are controlled by a \.{Makefile} +@*1 Temporary file output. Most \CEE/ projects are controlled by a \.{Makefile} that automatically takes care of the temporal dependencies between the different source modules. It may be convenient that \.{CWEB} doesn't create new output for all existing files, when there are only changes to some of them. Thus the @@ -696,7 +696,7 @@ if(check_file) fclose(check_file); if(strlen(check_file_name)) /* Delete the temporary file in case of a break */ remove(check_file_name); -@* Internationalization. If translation catalogs for your personal +@*1 Internationalization. If translation catalogs for your personal \.{LANGUAGE} are installed at the appropriate place, \.{CTANGLE} and \.{CWEAVE} will talk to you in your favorite language. Catalog \.{cweb} contains all strings from ``plain \.{CWEB},'' catalog \.{cweb-tl} contains a few extra @@ -759,7 +759,7 @@ free(texmf_locale); textdomain("cweb"); /* the majority of |"strings"| come from ``plain \.{CWEB}'' */ @.cweb.mo@> -@* File lookup with \Kpathsea/. The \.{CTANGLE} and \.{CWEAVE} programs from +@*1 File lookup with \Kpathsea/. The \.{CTANGLE} and \.{CWEAVE} programs from the original \.{CWEB} package use the compile-time default directory or the value of the environment variable \.{CWEBINPUTS} as an alternative place to be searched for files, if they could not be found in the current directory. @@ -816,7 +816,7 @@ numbers. Debugging output is always written to |stderr|, and begins with the string `\.{kdebug:}'. -@* System dependent changes. The most volatile stuff comes at the very end. +@*1 System dependent changes. The most volatile stuff comes at the very end. Modules for dealing with help messages and version info. diff --git a/Build/source/texk/web2c/cwebdir/comm-w2c.h b/Build/source/texk/web2c/cwebdir/comm-w2c.h index c2743c1a388..154371c4fa7 100644 --- a/Build/source/texk/web2c/cwebdir/comm-w2c.h +++ b/Build/source/texk/web2c/cwebdir/comm-w2c.h @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 (works also with later versions) +% Version 4.12 --- August 2024 (works also with later versions) % Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth diff --git a/Build/source/texk/web2c/cwebdir/common.h b/Build/source/texk/web2c/cwebdir/common.h index 97fda82e75a..2e02af934f4 100644 --- a/Build/source/texk/web2c/cwebdir/common.h +++ b/Build/source/texk/web2c/cwebdir/common.h @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 (works also with later versions) +% Version 4.12 --- August 2024 (works also with later versions) % Copyright (C) 1987,1990,1993 Silvio Levy and Donald E. Knuth diff --git a/Build/source/texk/web2c/cwebdir/common.w b/Build/source/texk/web2c/cwebdir/common.w index 88932f3118b..1c077736bb2 100644 --- a/Build/source/texk/web2c/cwebdir/common.w +++ b/Build/source/texk/web2c/cwebdir/common.w @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 +% Version 4.12 --- August 2024 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth @@ -22,12 +22,12 @@ \def\v{\char'174} % vertical (|) in typewriter font -\def\title{Common code for CTANGLE and CWEAVE (Version 4.11)} +\def\title{Common code for CTANGLE and CWEAVE (Version 4.12)} \def\topofcontents{\null\vfill \centerline{\titlefont Common code for {\ttitlefont CTANGLE} and {\ttitlefont CWEAVE}} \vskip 15pt - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} \vfill} \def\botofcontents{\vfill \noindent diff --git a/Build/source/texk/web2c/cwebdir/ctang-bs.ch b/Build/source/texk/web2c/cwebdir/ctang-bs.ch index c70a87fe1d0..234f41039e0 100644 --- a/Build/source/texk/web2c/cwebdir/ctang-bs.ch +++ b/Build/source/texk/web2c/cwebdir/ctang-bs.ch @@ -17,12 +17,12 @@ by using "huge" pointers. The ``banner line'' defined here should be changed whenever \.{CTANGLE} is modified. -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @y The ``banner line'' defined here should be changed whenever \.{CTANGLE} is modified. -@d banner "This is CTANGLE (Version 4.11pc/big)" +@d banner "This is CTANGLE (Version 4.12pc/big)" @z diff --git a/Build/source/texk/web2c/cwebdir/ctang-pc.ch b/Build/source/texk/web2c/cwebdir/ctang-pc.ch index 3e6e7ac5ef2..61db92aa7e4 100644 --- a/Build/source/texk/web2c/cwebdir/ctang-pc.ch +++ b/Build/source/texk/web2c/cwebdir/ctang-pc.ch @@ -9,9 +9,9 @@ that allows >64K arrays. If you need lots of bytes and toks, try the alternate change files with -bs suffix instead of -pc. @x section 1 -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @y -@d banner "This is CTANGLE (Version 4.11pc)" +@d banner "This is CTANGLE (Version 4.12pc)" @z @x section 17 @d max_bytes 100000 /* the number of bytes in identifiers, diff --git a/Build/source/texk/web2c/cwebdir/ctang-ql.ch b/Build/source/texk/web2c/cwebdir/ctang-ql.ch index 8072aff51ab..41831c850d9 100644 --- a/Build/source/texk/web2c/cwebdir/ctang-ql.ch +++ b/Build/source/texk/web2c/cwebdir/ctang-ql.ch @@ -7,15 +7,15 @@ ex <dev_>cc;"-v -h -c -=500000 ctangle_c" @x -\def\title{CTANGLE (Version 4.11)} +\def\title{CTANGLE (Version 4.12)} @y -\def\title{CTANGLE (QL Version 4.11)} +\def\title{CTANGLE (QL Version 4.12)} @z @x section 1 -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @y -@d banner "This is CTANGLE (QL Version 4.11)" +@d banner "This is CTANGLE (QL Version 4.12)" @z @x diff --git a/Build/source/texk/web2c/cwebdir/ctang-vms.ch b/Build/source/texk/web2c/cwebdir/ctang-vms.ch index 2c4df3bf60e..7f142df49a7 100644 --- a/Build/source/texk/web2c/cwebdir/ctang-vms.ch +++ b/Build/source/texk/web2c/cwebdir/ctang-vms.ch @@ -10,9 +10,9 @@ created: (these changes not necessary for initial bootstrapping) @x section 1 (01-FEB-1992 ST) -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @y -@d banner "This is CTANGLE (VAX/VMS Version 4.11)" +@d banner "This is CTANGLE (VAX/VMS Version 4.12)" @z @x section 5 (01-FEB-1992 ST) diff --git a/Build/source/texk/web2c/cwebdir/ctang-w2c.ch b/Build/source/texk/web2c/cwebdir/ctang-w2c.ch index 177daac7ab2..61eea96a0de 100644 --- a/Build/source/texk/web2c/cwebdir/ctang-w2c.ch +++ b/Build/source/texk/web2c/cwebdir/ctang-w2c.ch @@ -17,15 +17,15 @@ @q Please send comments, suggestions, etc. to tex-k@@tug.org. @> @x [0.0] l.30 -\def\title{CTANGLE (Version 4.11)} +\def\title{CTANGLE (Version 4.12)} @y -\def\title{CTANGLE (Version 4.11 [\TeX~Live])} +\def\title{CTANGLE (Version 4.12 [\TeX~Live])} @z @x [0.0] l.34 - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} @y - \centerline{(Version 4.11 [\TeX~Live])} + \centerline{(Version 4.12 [\TeX~Live])} @z @x [0.0] l.36 @@ -41,9 +41,9 @@ @z @x [1.1] l.64 -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @y -@d banner "This is CTANGLE, Version 4.11" +@d banner "This is CTANGLE, Version 4.12" /* will be extended by the \TeX~Live |versionstring| */ @z diff --git a/Build/source/texk/web2c/cwebdir/ctang-w32.ch b/Build/source/texk/web2c/cwebdir/ctang-w32.ch index c0384b48881..2a5c6f11868 100644 --- a/Build/source/texk/web2c/cwebdir/ctang-w32.ch +++ b/Build/source/texk/web2c/cwebdir/ctang-w32.ch @@ -4,9 +4,9 @@ This is the change file for CWEB's CTANGLE under Win32 Changes necessary for compiling with Borland C/C++ @x section 1 -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @y -@d banner "This is CTANGLE (Version 4.11win32)" +@d banner "This is CTANGLE (Version 4.12win32)" @z @x section 24 diff --git a/Build/source/texk/web2c/cwebdir/ctangle.c b/Build/source/texk/web2c/cwebdir/ctangle.c index 58e37b27b41..d1cf6c67694 100644 --- a/Build/source/texk/web2c/cwebdir/ctangle.c +++ b/Build/source/texk/web2c/cwebdir/ctangle.c @@ -15,7 +15,7 @@ /*:5*/ #line 67 "ctangle.w" -#define banner "This is CTANGLE (Version 4.11)" \ +#define banner "This is CTANGLE (Version 4.12)" \ #define ctangle false #define cweave true \ diff --git a/Build/source/texk/web2c/cwebdir/ctangle.w b/Build/source/texk/web2c/cwebdir/ctangle.w index 37b09e7fc02..b42595baa9e 100644 --- a/Build/source/texk/web2c/cwebdir/ctangle.w +++ b/Build/source/texk/web2c/cwebdir/ctangle.w @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 +% Version 4.12 --- August 2024 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth @@ -27,11 +27,11 @@ \mathchardef\RA="3221 % right arrow \mathchardef\BA="3224 % double arrow -\def\title{CTANGLE (Version 4.11)} +\def\title{CTANGLE (Version 4.12)} \def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont CTANGLE} processor} \vskip 15pt - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} \vfill} \def\botofcontents{\vfill \noindent @@ -61,7 +61,7 @@ Joachim Schrod, Lee Wittenberg, and others who have contributed improvements. The ``banner line'' defined here should be changed whenever \.{CTANGLE} is modified. -@d banner "This is CTANGLE (Version 4.11)" +@d banner "This is CTANGLE (Version 4.12)" @c @<Include files@>@/ diff --git a/Build/source/texk/web2c/cwebdir/ctwill-mini.ch b/Build/source/texk/web2c/cwebdir/ctwill-mini.ch index c5f7a3df85c..80e8feb3540 100644 --- a/Build/source/texk/web2c/cwebdir/ctwill-mini.ch +++ b/Build/source/texk/web2c/cwebdir/ctwill-mini.ch @@ -1589,9 +1589,9 @@ Section 212--213. Section 215. @x -@<Append a string or...@>={@+ int count=-1; /* characters remaining before string break */ +@<Append a \9{s}string or...@>={@+ int count=-1; /* characters remaining before string break */ @y -@<Append a string or...@>={int count=-1; /* characters remaining before string break */ +@<Append a \9{s}string or...@>={@+ int count=-1; /* characters remaining before string break */ @z Section 220. @@ -1819,12 +1819,12 @@ Section 247. @x @ @d usage_sentinel (struct perm_meaning *)1 -@<Translate the current section@>= { +@<Translate the \9{c}current section@>= { @y @ @d usage_sentinel (struct perm_meaning *)1 @-usage_sentinel@> @$usage_sentinel {CTWILL}247 =(\&{struct} \&{perm\_meaning} ${}{*}{}$) \T{1}@> -@<Translate the current section@>=@+ { +@<Translate the \9{c}current section@>= { @z Section 251. @@ -1851,9 +1851,9 @@ Section 252. Section 254. @x -@ @<Start a format...@>= { +@ @<Start \9{a}a format...@>= { @y -@r @ @<Start a format...@>= { +@r @ @<Start \9{a}a format...@>= { @z Section 255. diff --git a/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch b/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch index 70616695efb..d421c3f78cd 100644 --- a/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch +++ b/Build/source/texk/web2c/cwebdir/ctwill-w2c.ch @@ -34,38 +34,38 @@ \datethis % Put timestamp before section 1 @z -@x [0.0] l.35 -\def\title{CWEAVE (Version 4.11)} +@x [0.0] l.34 +\def\title{CWEAVE (Version 4.12)} @y \def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} \ifacro\sanitizecommand\Kpathsea{KPATHSEA}\fi -\def\title{CTWILL (Version 4.11 [\TeX~Live])} +\def\title{CTWILL (Version 4.12 [\TeX~Live])} @z -@x [0.0] l.37 +@x [0.0] l.36 \centerline{\titlefont The {\ttitlefont CWEAVE} processor} @y \centerline{\titlefont The {\ttitlefont CTWILL} processor} @z -@x [0.0] l.39 - \centerline{(Version 4.11)} +@x [0.0] l.38 + \centerline{(Version 4.12)} @y - \centerline{(Version 4.11 [\TeX~Live])} + \centerline{(Version 4.12 [\TeX~Live])} @z -@x [0.0] l.41 +@x [0.0] l.40 \def\botofcontents{\vfill @y \def\covernote{\vbox{% @z -@x [0.0] l.54 +@x [0.0] l.53 } @y }} @z -@x [1.1] l.59 +@x [1.1] l.58 This is the \.{CWEAVE} program by Silvio Levy and Donald E. Knuth, based on \.{WEAVE} by Knuth. We are thankful to Steve Avery, @@ -77,7 +77,7 @@ Crusius, and others who have contributed improvements. The ``banner line'' defined here should be changed whenever \.{CWEAVE} is modified. -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @y This is the \.{CTWILL} program by D. E. Knuth, based on \.{CWEAVE} by Silvio Levy and D.~E. Knuth. It is also based on @@ -101,29 +101,29 @@ Until then, \.{CWEAVE}'s sequence of sections will be preserved. The ``banner line'' defined here should be changed whenever \.{CTWILL} is modified. The version number parallels the corresponding version of \.{CWEAVE}. -@d banner "This is CTWILL, Version 4.11" +@d banner "This is CTWILL, Version 4.12" /* will be extended by the \TeX~Live |versionstring| */ @z -@x [1.2] l.94 +@x [1.2] l.93 program=cweave; @y program=ctwill; @z -@x [1.2] l.98 +@x [1.2] l.97 if (show_banner) puts(banner); /* print a ``banner line'' */ @y if (show_banner) cb_show_banner(); /* print a ``banner line'' */ @z -@x [1.3] l.112 +@x [1.3] l.111 @i common.h @y @i comm-w2c.h @z -@x [2.21] l.183 +@x [2.21] l.182 turned on during the first phase. @<Private...@>= @@ -132,19 +132,19 @@ static boolean change_exists; /* has any section changed? */ turned on during the first phase---NOT! @z -@x [2.23] l.217 +@x [2.23] l.216 @ @d max_refs 30000 /* number of cross-references; must be less than 65536 */ @y @ @d max_refs 65535 /* number of cross-references; must be less than 65536 */ @z -@x [2.25] l.246 +@x [2.25] l.245 @d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference"); @y @d append_xref(c) if (xref_ptr==xmem_end) overflow(_("cross-reference")); @z -@x [2.30] l.337 +@x [2.30] l.336 @d max_toks 30000 /* number of symbols in \CEE/ texts being parsed; must be less than 65536 */ @d max_texts 8000 /* number of phrases in \CEE/ texts being parsed; @@ -156,14 +156,14 @@ turned on during the first phase---NOT! must be less than 10240 */ @z -@x [2.32] l.357 +@x [2.32] l.356 @ Here are the two procedures needed to complete |id_lookup|: @y @ Here are the two procedures needed to complete |id_lookup|: @s perm_meaning int @z -@x [2.32] l.374 +@x [2.32] l.373 p->xref=(void *)xmem; @y p->xref=(void *)xmem; @@ -177,7 +177,7 @@ turned on during the first phase---NOT! } @z -@x [2.33] l.377 +@x [2.33] l.376 @ And here's a small helper function to simplify the code. @d update_node(p) (p)->xref=(void *)xref_ptr @@ -188,7 +188,7 @@ turned on during the first phase---NOT! @d get_meaning(p) (p)-name_dir+cur_meaning @z -@x [2.34] l.436 +@x [2.34] l.435 id_lookup("extern",NULL,int_like); @y @# @@ -196,7 +196,7 @@ ext_loc=id_lookup("extern",NULL,int_like)-name_dir; @# @z -@x [2.34] l.449 +@x [2.34] l.448 id_lookup("int",NULL,raw_int); @y @# @@ -204,7 +204,7 @@ int_loc=id_lookup("int",NULL,raw_int)-name_dir; @# @z -@x [3.36] l.544 +@x [3.36] l.543 @d ord 0207 /* control code for `\.{@@'}' */ @d join 0210 /* control code for `\.{@@\&}' */ @d thin_space 0211 /* control code for `\.{@@,}' */ @@ -248,7 +248,7 @@ int_loc=id_lookup("int",NULL,raw_int)-name_dir; @d new_section 0235 /* control code for `\.{@@\ }' and `\.{@@*}' */ @z -@x [3.38] l.587 +@x [3.38] l.586 ccode['\'']=ord; @y ccode['\'']=ord; @@ -256,7 +256,7 @@ ccode['$']=meaning; ccode['%']=temp_meaning; ccode['-']=suppress; ccode['r']=ccode['R']=right_start; @z -@x [3.40] l.608 +@x [3.40] l.607 static eight_bits skip_TeX(void); @ @c @@ -269,7 +269,7 @@ part of all meanings. @c @z -@x [3.41] l.614 +@x [3.41] l.613 if (loc>limit && get_line()==false) return; @y if (loc>limit && get_line()==false) return; @@ -279,44 +279,44 @@ part of all meanings. } @z -@x [4.43] l.670 +@x [4.43] l.669 \yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|, @y \yskip\hang |xref_roman|, |xref_wildcard|, |xref_typewriter|, |TeX_string|, |meaning|, |suppress|, @z -@x [4.46] l.731 +@x [4.46] l.730 @d right_preproc 0217 /* ends a preprocessor command */ @y @d right_preproc 0223 /* ends a preprocessor command */ @z -@x [4.57] l.888 +@x [4.57] l.887 err_print("! String didn't end"); loc=limit; break; @y err_print(_("! String didn't end")); loc=limit; break; @z -@x [4.57] l.892 +@x [4.57] l.891 err_print("! Input ended in middle of string"); loc=buffer; break; @y err_print(_("! Input ended in middle of string")); loc=buffer; break; @z -@x [4.57] l.911 +@x [4.57] l.910 printf("%s","\n! String too long: "); @y printf("%s",_("\n! String too long: ")); @z -@x [4.59] l.930 +@x [4.59] l.929 case translit_code: err_print("! Use @@l in limbo only"); continue; @y case translit_code: err_print(_("! Use @@l in limbo only")); continue; @z -@x [4.59] l.932 +@x [4.59] l.931 case underline: xref_switch=def_flag; continue; @y case underline: xref_switch=def_flag; continue; @@ -324,38 +324,38 @@ part of all meanings. case right_start: right_start_switch=true; continue; @z -@x [4.59] l.938 +@x [4.59] l.937 case xref_roman: case xref_wildcard: case xref_typewriter: case noop: @y case xref_roman: case xref_wildcard: case xref_typewriter: case noop: case meaning: case suppress: @z -@x [4.62] l.968 +@x [4.62] l.967 err_print("! Input ended in section name"); @y err_print(_("! Input ended in section name")); @z -@x [4.62] l.981 +@x [4.62] l.980 printf("%s","\n! Section name too long: "); @y printf("%s",_("\n! Section name too long: ")); @z -@x [4.63] l.995 +@x [4.63] l.994 err_print("! Section name didn't end"); break; @y err_print(_("! Section name didn't end")); break; @z -@x [4.63] l.999 +@x [4.63] l.998 err_print("! Control codes are forbidden in section name"); break; @y err_print(_("! Control codes are forbidden in section name")); break; @z -@x [4.64] l.1009 +@x [4.64] l.1008 skip_restricted(void) { @y @@ -363,19 +363,19 @@ skip_restricted(void) { int c=ccode[(eight_bits)*(loc-1)]; @z -@x [4.64] l.1016 +@x [4.64] l.1015 err_print("! Control text didn't end"); loc=limit; @y err_print(_("! Control text didn't end")); loc=limit; @z -@x [4.64] l.1021 +@x [4.64] l.1020 err_print("! Control codes are forbidden in control text"); @y err_print(_("! Control codes are forbidden in control text")); @z -@x [4.64] l.1022 +@x [4.64] l.1021 @.Control codes are forbidden...@> @y @.Control codes are forbidden...@> @@ -383,68 +383,68 @@ skip_restricted(void) else if (c==suppress && phase==2) @<Suppress mini-index entry@>@; @z -@x [4.66] l.1036 +@x [4.66] l.1035 if (loc>=limit) err_print("! Verbatim string didn't end"); @y if (loc>=limit) err_print(_("! Verbatim string didn't end")); @z -@x [5.68] l.1061 +@x [5.68] l.1060 skip_limbo(); change_exists=false; @y skip_limbo(); @<Give a default title to the program, if necessary@>@; @z -@x [5.68] l.1064 +@x [5.68] l.1063 changed_section[section_count]=change_exists; /* the index changes if anything does */ @y @z -@x [5.70] l.1073 +@x [5.70] l.1072 if (++section_count==max_sections) overflow("section number"); @y if (++section_count==max_sections) overflow(_("section number")); @z -@x [5.70] l.1074 +@x [5.70] l.1073 changed_section[section_count]=changing; /* it will become |true| if any line changes */ @y @z -@x [5.70] l.1083 +@x [5.70] l.1082 if (changed_section[section_count]) change_exists=true; @y @z -@x [5.74] l.1162 +@x [5.74] l.1161 case translit_code: err_print("! Use @@l in limbo only"); continue; @y case translit_code: err_print(_("! Use @@l in limbo only")); continue; @z -@x [5.74] l.1167 +@x [5.74] l.1166 case xref_roman: case xref_wildcard: case xref_typewriter: @y case xref_roman: case xref_wildcard: case xref_typewriter: case meaning: case suppress: @z -@x [5.79] l.1249 +@x [5.79] l.1248 err_print("! Missing left identifier of @@s"); @y err_print(_("! Missing left identifier of @@s")); @z -@x [5.79] l.1254 +@x [5.79] l.1253 err_print("! Missing right identifier of @@s"); @y err_print(_("! Missing right identifier of @@s")); @z -@x [5.82] l.1302 +@x [5.82] l.1301 printf("%s","\n! Never defined: <"); print_section_name(p); putchar('>'); mark_harmless(); @y @@ -452,7 +452,7 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end")); print_section_name(p); putchar('>'); mark_harmless(); @z -@x [5.82] l.1308 +@x [5.82] l.1307 printf("%s","\n! Never used: <"); print_section_name(p); putchar('>'); mark_harmless(); @y @@ -460,13 +460,13 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end")); print_section_name(p); putchar('>'); mark_harmless(); @z -@x [6.89] l.1397 +@x [6.89] l.1396 @ In particular, the |finish_line| procedure is called near the very beginning of phase two. We initialize the output variables in a slightly tricky way so that the first line of the output file will be `\.{\\input cwebmac}'. -@<Start \TEX/...@>= +@<Start \9{t}\TEX/...@>= out_ptr=out_buf+1; out_line=1; active_file=tex_file; tex_printf("\\input cwebma"); *out_ptr='c'; @y @@ -486,7 +486,7 @@ debugging mini-index entries. @d proofing flags['P'] -@<Start \TEX/...@>= +@<Start \9{t}\TEX/...@>= out_ptr=out_buf+1; out_line=1; active_file=tex_file; tex_puts("\\input "); tex_printf(use_language); @@ -494,7 +494,7 @@ tex_puts(proofing?"ctproofma":"ctwima"); *out_ptr='c'; @z -@x [6.90] l.1414 +@x [6.90] l.1413 @d out(c) {if (out_ptr>=out_buf_end) break_out(); *(++out_ptr)=c;} @y @d out(c) @@ -508,102 +508,102 @@ tex_puts(proofing?"ctproofma":"ctwima"); } @z -@x [6.94] l.1462 +@x [6.94] l.1461 printf("\n! Line had to be broken (output l. %d):\n",out_line); @y printf(_("\n! Line had to be broken (output l. %d):\n"),out_line); @z -@x [6.95] l.1471 +@x [6.95] l.1470 |def_flag|, so it cannot have more than five decimal digits. If the section is changed, we output `\.{\\*}' just after the number. @y |def_flag|, so it cannot have more than five decimal digits. @z -@x [6.96] l.1485 +@x [6.96] l.1484 if (changed_section[n]) out_str("\\*"); @.\\*@> @y @z -@x [7.99] l.1543 +@x [7.99] l.1542 default: err_print("! Double @@ should be used in limbo"); @y case right_start: right_start_switch=true; break; default: err_print(_("! Double @@ should be used in limbo")); @z -@x [7.101] l.1585 +@x [7.101] l.1584 @d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;} @y @d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow(_("token")); *(tok_ptr++)=c;} @z -@x [7.101] l.1597 +@x [7.101] l.1596 err_print("! Input ended in mid-comment"); @y err_print(_("! Input ended in mid-comment")); @z -@x [7.101] l.1602 +@x [7.101] l.1601 if (bal>1) err_print("! Missing } in comment"); @y if (bal>1) err_print(_("! Missing } in comment")); @z -@x [7.101] l.1618 +@x [7.101] l.1617 else {err_print("! Extra } in comment"); @y else {err_print(_("! Extra } in comment")); @z -@x [7.102] l.1630 +@x [7.102] l.1629 if (bal>1) err_print("! Missing } in comment"); @y if (bal>1) err_print(_("! Missing } in comment")); @z -@x [7.103] l.1638 +@x [7.103] l.1637 err_print("! Illegal use of @@ in comment"); @y err_print(_("! Illegal use of @@ in comment")); @z -@x [8.106] l.1755 +@x [8.106] l.1754 @d attr_head 69 /* denotes beginning of attribute */ @y @d attr_head 69 /* denotes beginning of attribute */ @d title 70 /* program name or header name in a ``meaning'' */ @z -@x [8.110] l.2129 +@x [8.110] l.2128 @i prod.w @y @i prod-twill.w @z -@x [9.113] l.2179 +@x [9.113] l.2178 static scrap scrap_info[max_scraps]; /* memory array for scraps */ @y static scrap scrap_info[max_scraps]; /* memory array for scraps */ static scrap null_scrap; /* a scrap with empty translation */ @z -@x [9.114] l.2188 +@x [9.114] l.2187 @ @<Set init...@>= @y @ @<Set init...@>= null_scrap.trans=&tok_start[0]; @z -@x [9.116] l.2249 +@x [9.116] l.2248 update_terminal(); @y puts("|"); update_terminal(); @z -@x [9.124] l.2560 +@x [9.124] l.2559 the |for| loop below. @c @@ -618,7 +618,7 @@ static token_pointer tok_loc; /* where the first identifier appears */ @# @z -@x [9.124] l.2568 +@x [9.124] l.2567 token_pointer tok_loc; /* pointer to |tok_value| */ if ((tok_loc=find_first_ident(p->trans))<=operator_found) return; /* this should not happen */ @@ -626,12 +626,12 @@ static token_pointer tok_loc; /* where the first identifier appears */ if (tok_loc<=operator_found) return; /* this should not happen */ @z -@x [9.125] l.2597 +@x [9.125] l.2596 token_pointer tok_loc; /* where the first identifier appears */ @y @z -@x [9.128] l.2651 +@x [9.128] l.2650 @<Cases for |exp|@>= if (cat1==lbrace || cat1==int_like || cat1==decl) { make_underlined(pp); big_app(dindent); big_app1(pp); @@ -646,7 +646,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) { } @z -@x [9.128] l.2666 +@x [9.128] l.2665 make_underlined (pp); squash(pp,2,tag,-1,7); @y make_underlined (pp); @@ -657,7 +657,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) { squash(pp,2,tag,-1,7); @z -@x [9.138] l.2755 +@x [9.138] l.2754 @ @<Cases for |decl_head|@>= if (cat1==comma) { big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33); @@ -700,7 +700,7 @@ else if (cat1==int_like || cat1==lbrace || cat1==decl) { else if (cat1==semi) squash(pp,2,decl,-1,39); @z -@x [9.139] l.2777 +@x [9.139] l.2776 @ @<Cases for |decl|@>= if (cat1==decl) { big_app1_insert(pp,force); reduce(pp,2,decl,-1,40); @@ -719,13 +719,13 @@ else if (cat1==stmt || cat1==function) { } @z -@x [9.141] l.2803 +@x [9.141] l.2802 make_underlined(pp+1); make_reserved(pp+1); @y make_underlined(pp+1); make_reserved(pp+1); make_ministring(pp+1); @z -@x [9.143] l.2834 +@x [9.143] l.2833 @ @<Cases for |fn_decl|@>= if (cat1==decl) { big_app1_insert(pp,force); reduce(pp,2,fn_decl,0,51); @@ -748,14 +748,14 @@ else if (cat1==stmt) { } @z -@x [9.153] l.2938 +@x [9.153] l.2937 big_app1_insert(pp,dindent); reduce(pp,2,fn_decl,0,73); @y big_app1(pp); if (indent_param_decl) big_app(dindent); big_app1(pp+1); reduce(pp,2,fn_decl,0,73); @z -@x [9.156] l.2964 +@x [9.156] l.2963 big_app1_insert(pp, (cat1==decl || cat1==function) ? big_force : force_lines ? force : break_space); reduce(pp,2,cat1,-1,76); @y @@ -764,7 +764,7 @@ else if (cat1==stmt) { ( force_lines ? force : break_space ) ); reduce(pp,2,cat1,-1,76); @z -@x [9.158] l.2972 +@x [9.158] l.2971 if (cat1==define_like) make_underlined(pp+2); @y if (cat1==define_like) { /* \#\&{define} is analogous to \&{extern} */ @@ -776,7 +776,7 @@ if (cat1==define_like) { /* \#\&{define} is analogous to \&{extern} */ } @z -@x [9.167] l.3069 +@x [9.167] l.3068 if (cat1==prelangle) reduce(pp+1,0,langle,1,100); else reduce(pp,0,exp,-2,101); @y @@ -784,13 +784,13 @@ if (cat1==prelangle) reduce(pp+1,0,langle,1,121); else reduce(pp,0,exp,-2,122); @z -@x [9.168] l.3074 +@x [9.168] l.3073 big_app1_insert(pp,' '); reduce(pp,2,else_like,-2,102); @y big_app1_insert(pp,' '); reduce(pp,2,else_like,-2,123); @z -@x [9.173] l.3106 +@x [9.173] l.3105 @ @<Cases for |typedef_like|@>= if ((cat1==int_like || cat1==cast) && (cat2==comma || cat2==semi)) reduce(pp+1,0,exp,-1,115); @@ -826,56 +826,56 @@ if (cat1==decl_head) { (cat3==semi || cat3==comma)) reduce(pp+2,0,exp,1,202); @z -@x [9.186] l.3293 +@x [9.186] l.3292 overflow("token"); @y overflow(_("token")); @z -@x [9.186] l.3297 +@x [9.186] l.3296 overflow("text"); @y overflow(_("text")); @z -@x [9.190] l.3352 +@x [9.190] l.3351 if (tok_ptr+6>tok_mem_end) overflow("token"); @y if (tok_ptr+6>tok_mem_end) overflow(_("token")); @z -@x [9.191] l.3358 +@x [9.191] l.3357 printf("\nIrreducible scrap sequence in section %d:",(int)section_count); @y printf(_("\nIrreducible scrap sequence in section %d:"),(int)section_count); @z -@x [9.192] l.3368 +@x [9.192] l.3367 printf("\nTracing after l. %d:\n",cur_line); mark_harmless(); @y printf(_("\nTracing after l. %d:\n"),cur_line); mark_harmless(); @z -@x [10.196] l.3454 +@x [10.196] l.3453 case ignore: case xref_roman: case xref_wildcard: @y case ignore: case xref_roman: case xref_wildcard: case meaning: case suppress: @z -@x [10.197] l.3500 +@x [10.197] l.3499 overflow("scrap/token/text"); @y overflow(_("scrap/token/text")); @z -@x [10.199] l.3585 +@x [10.199] l.3584 else err_print("! Double @@ should be used in strings"); @y else err_print(_("! Double @@ should be used in strings")); @z -@x [10.202] l.3650 +@x [10.202] l.3649 else app_scrap(p->ilk,maybe_math); } } @@ -886,62 +886,62 @@ if (cat1==decl_head) { @<Flag the usage of this identifier, for the mini-index@>@; @z -@x [10.203] l.3668 +@x [10.203] l.3667 if (next_control!='|') err_print("! Missing '|' after C text"); @y if (next_control!='|') err_print(_("! Missing '|' after C text")); @z -@x [10.204] l.3683 +@x [10.204] l.3682 to \.{\\PB}. Although \.{cwebmac} ignores \.{\\PB}, other macro packages @y to \.{\\PB}. Although \.{ctwimac} ignores \.{\\PB}, other macro packages @z -@x [10.205] l.3713 +@x [10.205] l.3712 app(tok_flag+(int)(p-tok_start)); @y app(tok_flag+(int)(p-tok_start)); app(inserted); @z -@x [11.211] l.3809 +@x [11.211] l.3808 if (stack_ptr==stack_end) overflow("stack"); @y if (stack_ptr==stack_end) overflow(_("stack")); @z -@x [11.224] l.4132 +@x [11.224] l.4131 printf("%s","\n! Illegal control code in section name: <"); @y printf("%s",_("\n! Illegal control code in section name: <")); @z -@x [11.225] l.4147 +@x [11.225] l.4146 printf("%s","\n! C text in section name didn't end: <"); @y printf("%s",_("\n! C text in section name didn't end: <")); @z -@x [11.225] l.4159 +@x [11.225] l.4158 if (j>buffer+long_buf_size-3) overflow("buffer"); @y if (j>buffer+long_buf_size-3) overflow(_("buffer")); @z -@x [11.226] l.4167 +@x [11.226] l.4166 if (j>buffer+long_buf_size-4) overflow("buffer"); @y if (j>buffer+long_buf_size-4) overflow(_("buffer")); @z -@x [12.227] l.4182 +@x [12.227] l.4181 if (show_progress) printf("%s","\nWriting the output file..."); @y if (show_progress) printf("%s",_("\nWriting the output file...")); @z -@x [12.227] l.4184 +@x [12.227] l.4183 section_count=0; format_visible=true; copy_limbo(); @y temp_switch=false; temp_meaning_ptr=temp_meaning_stack; @@ -949,10 +949,10 @@ temp_switch=false; temp_meaning_ptr=temp_meaning_stack; section_count=0; format_visible=true; right_start_switch=false; copy_limbo(); @z -@x [12.229] l.4213 +@x [12.229] l.4212 static boolean group_found=false; /* has a starred section occurred? */ -@ @<Translate the current section@>= { +@ @<Translate the \9{c}current section@>= { section_count++; @y static boolean group_found=false; /* has a starred section occurred? */ @@ -960,18 +960,18 @@ static boolean right_start_switch; /* has `\.{@@r}' occurred recently? */ static boolean temp_switch; /* has `\.{@@\%}' occurred recently? */ @ @d usage_sentinel (struct perm_meaning *)1 -@<Translate the current section@>= { +@<Translate the \9{c}current section@>= { section_count++; temp_switch=false; temp_meaning_ptr=temp_meaning_stack; top_usage=usage_sentinel; @z -@x [12.231] l.4231 +@x [12.231] l.4230 If the section has changed, we put \.{\\*} just after the section number. @y @z -@x [12.231] l.4234 +@x [12.231] l.4233 if (*(loc-1)!='*') out_str("\\M"); @y if (*(loc-1)!='*') { @@ -983,7 +983,7 @@ if (*(loc-1)!='*') { } @z -@x [12.231] l.4249 +@x [12.231] l.4248 @.\\N@> @y @.\\N@> @@ -993,14 +993,14 @@ if (*(loc-1)!='*') { } @z -@x [12.231] l.4256 +@x [12.231] l.4255 out('{'); out_section(section_count); out('}'); @y out('{'); out_section(section_count); out('}'); flush_buffer(out_ptr,false,false); @z -@x [12.232] l.4264 +@x [12.232] l.4263 case '@@': out('@@'); break; @y case '@@': out('@@'); break; @@ -1008,26 +1008,26 @@ flush_buffer(out_ptr,false,false); case right_start: right_start_switch=true; break; @z -@x [12.232] l.4267 +@x [12.232] l.4266 case section_name: loc-=2; next_control=get_next(); /* skip to \.{@@>} */ @y case meaning: case suppress: case section_name: loc-=2; next_control=get_next(); /* reprocess */ @z -@x [12.232] l.4269 +@x [12.232] l.4268 err_print("! TeX string should be in C text only"); break; @y err_print(_("! TeX string should be in C text only")); break; @z -@x [12.232] l.4275 +@x [12.232] l.4274 err_print("! You can't do that in TeX text"); break; @y err_print(_("! You can't do that in TeX text")); break; @z -@x [12.233] l.4289 +@x [12.233] l.4288 outer_parse(); finish_C(format_visible); format_visible=true; doing_format=false; } @@ -1039,20 +1039,20 @@ flush_buffer(out_ptr,false,false); } @z -@x [12.236] l.4341 -@<Start a macro...@>= { +@x [12.236] l.4340 +@<Start \9{a}a macro...@>= { @y -@<Start a macro...@>= { +@<Start \9{a}a macro...@>= { is_macro=true; @z -@x [12.236] l.4347 +@x [12.236] l.4346 err_print("! Improper macro definition"); @y err_print(_("! Improper macro definition")); @z -@x [12.236] l.4348 +@x [12.236] l.4347 @.Improper macro definition@> else { app('$'); app_cur_id(false); @@ -1064,7 +1064,7 @@ flush_buffer(out_ptr,false,false); def_diff=(*loc!='('); @z -@x [12.236] l.4360 +@x [12.236] l.4359 } @=/* otherwise fall through */@>@; default: err_print("! Improper macro definition"); break; @y @@ -1072,34 +1072,34 @@ flush_buffer(out_ptr,false,false); default: err_print(_("! Improper macro definition")); break; @z -@x [12.237] l.4370 -@ @<Start a format...@>= { +@x [12.237] l.4369 +@ @<Start \9{a}a format...@>= { doing_format=true; @y -@ @<Start a format...@>= { +@ @<Start \9{a}a format...@>= { doing_format=true; is_macro=false; @z -@x [12.237] l.4387 +@x [12.237] l.4386 if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition"); @y if (scrap_ptr!=scrap_info+2) err_print(_("! Improper format definition")); @z -@x [12.240] l.4422 +@x [12.240] l.4421 err_print("! You need an = sign after the section name"); @y err_print(_("! You need an = sign after the section name")); @z -@x [12.241] l.4444 +@x [12.241] l.4443 err_print("! You can't do that in C text"); @y err_print(_("! You can't do that in C text")); @z -@x [12.246] l.4514 +@x [12.246] l.4513 out_str("\\fi"); finish_line(); @.\\fi@> @y @@ -1110,13 +1110,13 @@ out_str("}\\FI"); finish_line(); @.\\FI@> @z -@x [13.247] l.4535 +@x [13.247] l.4534 if (show_progress) printf("%s","\nWriting the index..."); @y if (show_progress) printf("%s",_("\nWriting the index...")); @z -@x [13.247] l.4537 +@x [13.247] l.4536 if (change_exists) { @<Tell about changed sections@>@; finish_line(); flush_buffer(out_buf,false,false); @@ -1125,32 +1125,32 @@ out_str("}\\FI"); finish_line(); @y @z -@x [13.247] l.4546 +@x [13.247] l.4545 fatal("! Cannot open index file ",idx_file_name); @y fatal(_("! Cannot open index file "),idx_file_name); @z -@x [13.247] l.4558 +@x [13.247] l.4557 fatal("! Cannot open section file ",scn_file_name); @y fatal(_("! Cannot open section file "),scn_file_name); @z -@x [13.247] l.4570 +@x [13.247] l.4569 fclose(active_file); @y fclose(active_file); active_file=tex_file=NULL; if (check_for_change) @<Update the result when it has changed@>@; @z -@x [13.247] l.4573 +@x [13.247] l.4572 printf("%s","Done."); @y printf("%s",_("Done.")); @z -@x [13.249] l.4581 +@x [13.249] l.4580 the index section itself. @<Tell about changed sections@>= @@ -1169,13 +1169,13 @@ out('.'); the index section itself---NOT! @z -@x [13.257] l.4725 +@x [13.257] l.4724 if (sort_ptr>=scrap_info_end) overflow("sorting"); @y if (sort_ptr>=scrap_info_end) overflow(_("sorting")); @z -@x [13.262] l.4770 +@x [13.262] l.4769 @ @<Output the name...@>= switch (cur_name->ilk) {@+char *p; /* index into |byte_mem| */@+@t}\6{\4@> @y @@ -1186,20 +1186,20 @@ rest of the job. Compare this code with section |@<Mini-output...@>|. switch (cur_name->ilk) { @z -@x [13.262] l.4772 +@x [13.262] l.4771 case normal: case func_template: @y case normal: @z -@x [13.262] l.4774 +@x [13.262] l.4773 else {@+boolean all_caps=true;@+@t}\6{@> @y else {@+boolean all_caps=true;@+char *p; /* index into |byte_mem| */ @+@t}\6{@> @z -@x [13.262] l.4789 +@x [13.262] l.4788 case roman: not_an_identifier: out_name(cur_name,false); goto name_done; case custom: out_str("$\\"); @@ -1214,13 +1214,13 @@ not_an_identifier: out_name(cur_name,false); goto name_done; @.\\\$@> @z -@x [13.262] l.4799 +@x [13.262] l.4798 out_name(cur_name,true); @y out_name(cur_name,proofing); @z -@x [13.269] l.4862 +@x [13.269] l.4861 puts("\nMemory usage statistics:"); @.Memory usage statistics:@> printf("%td names (out of %ld)\n",@^system dependencies@> @@ -1269,7 +1269,7 @@ out_name(cur_name,proofing); (ptrdiff_t)(max_sort_ptr-scrap_info),(long)max_scraps); @z -@x [14.270] l.4884 +@x [14.270] l.4883 @** Index. @y @q Section 270. @> diff --git a/Build/source/texk/web2c/cwebdir/cweav-bs.ch b/Build/source/texk/web2c/cwebdir/cweav-bs.ch index c2f3214c793..458a0a7e60c 100644 --- a/Build/source/texk/web2c/cwebdir/cweav-bs.ch +++ b/Build/source/texk/web2c/cwebdir/cweav-bs.ch @@ -20,12 +20,12 @@ This file contributed by Barry Schwartz, trashman@crud.mn.org, 28 Jun 94. The ``banner line'' defined here should be changed whenever \.{CWEAVE} is modified. -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @y The ``banner line'' defined here should be changed whenever \.{CWEAVE} is modified. -@d banner "This is CWEAVE (Version 4.11pc/big)" +@d banner "This is CWEAVE (Version 4.12pc/big)" @z diff --git a/Build/source/texk/web2c/cwebdir/cweav-pc.ch b/Build/source/texk/web2c/cwebdir/cweav-pc.ch index 165b8a54eeb..ae1fb9f45c7 100644 --- a/Build/source/texk/web2c/cwebdir/cweav-pc.ch +++ b/Build/source/texk/web2c/cwebdir/cweav-pc.ch @@ -11,9 +11,9 @@ that allows >64K arrays. (If you need lots more bytes, try the alternate change files that have -bs in their name instead of -pc.) @x section 1 -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @y -@d banner "This is CWEAVE (Version 4.11pc)" +@d banner "This is CWEAVE (Version 4.12pc)" @z @x section 17 diff --git a/Build/source/texk/web2c/cwebdir/cweav-ql.ch b/Build/source/texk/web2c/cwebdir/cweav-ql.ch index 31bd6138a29..fd369064deb 100644 --- a/Build/source/texk/web2c/cwebdir/cweav-ql.ch +++ b/Build/source/texk/web2c/cwebdir/cweav-ql.ch @@ -7,15 +7,15 @@ ex <dev_>cc;"-v -h -c =500000 cweave_c" @x -\def\title{CWEAVE (Version 4.11)} +\def\title{CWEAVE (Version 4.12)} @y -\def\title{CWEAVE (QL Version 4.11)} +\def\title{CWEAVE (QL Version 4.12)} @z @x section 1 -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @y -@d banner "This is CWEAVE (QL Version 4.11)" +@d banner "This is CWEAVE (QL Version 4.12)" @z @x diff --git a/Build/source/texk/web2c/cwebdir/cweav-vms.ch b/Build/source/texk/web2c/cwebdir/cweav-vms.ch index b944f838153..f4630f98661 100644 --- a/Build/source/texk/web2c/cwebdir/cweav-vms.ch +++ b/Build/source/texk/web2c/cwebdir/cweav-vms.ch @@ -13,9 +13,9 @@ modified: (also modified by Don Knuth to keep version numbers uptodate) @x section 1 (01-FEB-1992 ST) -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @y -@d banner "This is CWEAVE (VAX/VMS Version 4.11)" +@d banner "This is CWEAVE (VAX/VMS Version 4.12)" @z @x section 5 (01-FEB-1992 ST) diff --git a/Build/source/texk/web2c/cwebdir/cweav-w2c.ch b/Build/source/texk/web2c/cwebdir/cweav-w2c.ch index 73b9ab4b2c9..b0adceab75a 100644 --- a/Build/source/texk/web2c/cwebdir/cweav-w2c.ch +++ b/Build/source/texk/web2c/cwebdir/cweav-w2c.ch @@ -16,62 +16,62 @@ @q Please send comments, suggestions, etc. to tex-k@@tug.org. @> -@x [0.0] l.35 -\def\title{CWEAVE (Version 4.11)} +@x [0.0] l.34 +\def\title{CWEAVE (Version 4.12)} @y -\def\title{CWEAVE (Version 4.11 [\TeX~Live])} +\def\title{CWEAVE (Version 4.12 [\TeX~Live])} @z -@x [0.0] l.39 - \centerline{(Version 4.11)} +@x [0.0] l.38 + \centerline{(Version 4.12)} @y - \centerline{(Version 4.11 [\TeX~Live])} + \centerline{(Version 4.12 [\TeX~Live])} @z -@x [0.0] l.41 +@x [0.0] l.40 \def\botofcontents{\vfill @y \def\covernote{\vbox{% @z -@x [0.0] l.54 +@x [0.0] l.53 } @y }}\datecontentspage @z -@x [1.1] l.70 -@d banner "This is CWEAVE (Version 4.11)" +@x [1.1] l.69 +@d banner "This is CWEAVE (Version 4.12)" @y -@d banner "This is CWEAVE, Version 4.11" +@d banner "This is CWEAVE, Version 4.12" /* will be extended by the \TeX~Live |versionstring| */ @z -@x [1.2] l.98 +@x [1.2] l.97 if (show_banner) puts(banner); /* print a ``banner line'' */ @y if (show_banner) cb_show_banner(); /* print a ``banner line'' */ @z -@x [1.3] l.112 +@x [1.3] l.111 @i common.h @y @i comm-w2c.h @z -@x [2.23] l.217 +@x [2.23] l.216 @ @d max_refs 30000 /* number of cross-references; must be less than 65536 */ @y @ @d max_refs 65535 /* number of cross-references; must be less than 65536 */ @z -@x [2.25] l.246 +@x [2.25] l.245 @d append_xref(c) if (xref_ptr==xmem_end) overflow("cross-reference"); @y @d append_xref(c) if (xref_ptr==xmem_end) overflow(_("cross-reference")); @z -@x [2.30] l.337 +@x [2.30] l.336 @d max_toks 30000 /* number of symbols in \CEE/ texts being parsed; must be less than 65536 */ @d max_texts 8000 /* number of phrases in \CEE/ texts being parsed; @@ -83,97 +83,97 @@ must be less than 10240 */ @z -@x [4.57] l.888 +@x [4.57] l.887 err_print("! String didn't end"); loc=limit; break; @y err_print(_("! String didn't end")); loc=limit; break; @z -@x [4.57] l.892 +@x [4.57] l.891 err_print("! Input ended in middle of string"); loc=buffer; break; @y err_print(_("! Input ended in middle of string")); loc=buffer; break; @z -@x [4.57] l.911 +@x [4.57] l.910 printf("%s","\n! String too long: "); @y printf("%s",_("\n! String too long: ")); @z -@x [4.59] l.930 +@x [4.59] l.929 case translit_code: err_print("! Use @@l in limbo only"); continue; @y case translit_code: err_print(_("! Use @@l in limbo only")); continue; @z -@x [4.62] l.968 +@x [4.62] l.967 err_print("! Input ended in section name"); @y err_print(_("! Input ended in section name")); @z -@x [4.62] l.981 +@x [4.62] l.980 printf("%s","\n! Section name too long: "); @y printf("%s",_("\n! Section name too long: ")); @z -@x [4.63] l.995 +@x [4.63] l.994 err_print("! Section name didn't end"); break; @y err_print(_("! Section name didn't end")); break; @z -@x [4.63] l.999 +@x [4.63] l.998 err_print("! Control codes are forbidden in section name"); break; @y err_print(_("! Control codes are forbidden in section name")); break; @z -@x [4.64] l.1016 +@x [4.64] l.1015 err_print("! Control text didn't end"); loc=limit; @y err_print(_("! Control text didn't end")); loc=limit; @z -@x [4.64] l.1021 +@x [4.64] l.1020 err_print("! Control codes are forbidden in control text"); @y err_print(_("! Control codes are forbidden in control text")); @z -@x [4.66] l.1036 +@x [4.66] l.1035 if (loc>=limit) err_print("! Verbatim string didn't end"); @y if (loc>=limit) err_print(_("! Verbatim string didn't end")); @z -@x [5.70] l.1073 +@x [5.70] l.1072 if (++section_count==max_sections) overflow("section number"); @y if (++section_count==max_sections) overflow(_("section number")); @z -@x [5.74] l.1162 +@x [5.74] l.1161 case translit_code: err_print("! Use @@l in limbo only"); continue; @y case translit_code: err_print(_("! Use @@l in limbo only")); continue; @z -@x [5.79] l.1249 +@x [5.79] l.1248 err_print("! Missing left identifier of @@s"); @y err_print(_("! Missing left identifier of @@s")); @z -@x [5.79] l.1254 +@x [5.79] l.1253 err_print("! Missing right identifier of @@s"); @y err_print(_("! Missing right identifier of @@s")); @z -@x [5.82] l.1302 +@x [5.82] l.1301 printf("%s","\n! Never defined: <"); print_section_name(p); putchar('>'); mark_harmless(); @y @@ -181,7 +181,7 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end")); print_section_name(p); putchar('>'); mark_harmless(); @z -@x [5.82] l.1308 +@x [5.82] l.1307 printf("%s","\n! Never used: <"); print_section_name(p); putchar('>'); mark_harmless(); @y @@ -189,13 +189,13 @@ if (loc>=limit) err_print(_("! Verbatim string didn't end")); print_section_name(p); putchar('>'); mark_harmless(); @z -@x [6.89] l.1397 +@x [6.89] l.1396 @ In particular, the |finish_line| procedure is called near the very beginning of phase two. We initialize the output variables in a slightly tricky way so that the first line of the output file will be `\.{\\input cwebmac}'. -@<Start \TEX/...@>= +@<Start \9{t}\TEX/...@>= out_ptr=out_buf+1; out_line=1; active_file=tex_file; tex_printf("\\input cwebma"); *out_ptr='c'; @y @@ -210,7 +210,7 @@ prepended to `\.{cwebmac.tex}', e.g., if you call \.{CWEAVE} with Without this option the first line of the output file will be `\.{\\input cwebmac}'. -@<Start \TEX/...@>= +@<Start \9{t}\TEX/...@>= out_ptr=out_buf+1; out_line=1; active_file=tex_file; tex_puts("\\input "); tex_printf(use_language); @@ -218,61 +218,61 @@ tex_puts("cwebma"); *out_ptr='c'; @z -@x [6.94] l.1462 +@x [6.94] l.1461 printf("\n! Line had to be broken (output l. %d):\n",out_line); @y printf(_("\n! Line had to be broken (output l. %d):\n"),out_line); @z -@x [7.99] l.1543 +@x [7.99] l.1542 default: err_print("! Double @@ should be used in limbo"); @y default: err_print(_("! Double @@ should be used in limbo")); @z -@x [7.101] l.1585 +@x [7.101] l.1584 @d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow("token"); *(tok_ptr++)=c;} @y @d app_tok(c) {if (tok_ptr+2>tok_mem_end) overflow(_("token")); *(tok_ptr++)=c;} @z -@x [7.101] l.1597 +@x [7.101] l.1596 err_print("! Input ended in mid-comment"); @y err_print(_("! Input ended in mid-comment")); @z -@x [7.101] l.1602 +@x [7.101] l.1601 if (bal>1) err_print("! Missing } in comment"); @y if (bal>1) err_print(_("! Missing } in comment")); @z -@x [7.101] l.1618 +@x [7.101] l.1617 else {err_print("! Extra } in comment"); @y else {err_print(_("! Extra } in comment")); @z -@x [7.102] l.1630 +@x [7.102] l.1629 if (bal>1) err_print("! Missing } in comment"); @y if (bal>1) err_print(_("! Missing } in comment")); @z -@x [7.103] l.1638 +@x [7.103] l.1637 err_print("! Illegal use of @@ in comment"); @y err_print(_("! Illegal use of @@ in comment")); @z -@x [8.110] l.2129 +@x [8.110] l.2128 @i prod.w @y @i prod-cweave.w @z -@x [9.128] l.2651 +@x [9.128] l.2650 @<Cases for |exp|@>= if (cat1==lbrace || cat1==int_like || cat1==decl) { make_underlined(pp); big_app(dindent); big_app1(pp); @@ -286,7 +286,7 @@ if(cat1==lbrace || cat1==int_like || cat1==decl) { } @z -@x [9.138] l.2755 +@x [9.138] l.2754 @ @<Cases for |decl_head|@>= if (cat1==comma) { big_app2(pp); app(opt); app('9'); reduce(pp,2,decl_head,-1,33); @@ -328,7 +328,7 @@ else if (cat1==int_like || cat1==lbrace || cat1==decl) { else if (cat1==semi) squash(pp,2,decl,-1,39); @z -@x [9.139] l.2777 +@x [9.139] l.2776 @ @<Cases for |decl|@>= if (cat1==decl) { big_app1_insert(pp,force); reduce(pp,2,decl,-1,40); @@ -347,7 +347,7 @@ else if (cat1==stmt || cat1==function) { } @z -@x [9.143] l.2834 +@x [9.143] l.2833 @ @<Cases for |fn_decl|@>= if (cat1==decl) { big_app1_insert(pp,force); reduce(pp,2,fn_decl,0,51); @@ -370,14 +370,14 @@ else if (cat1==stmt) { } @z -@x [9.153] l.2938 +@x [9.153] l.2937 big_app1_insert(pp,dindent); reduce(pp,2,fn_decl,0,73); @y big_app1(pp); if (indent_param_decl) big_app(dindent); big_app1(pp+1); reduce(pp,2,fn_decl,0,73); @z -@x [9.156] l.2964 +@x [9.156] l.2963 big_app1_insert(pp, (cat1==decl || cat1==function) ? big_force : force_lines ? force : break_space); reduce(pp,2,cat1,-1,76); @y @@ -386,109 +386,109 @@ else if (cat1==stmt) { ( force_lines ? force : break_space ) ); reduce(pp,2,cat1,-1,76); @z -@x [9.186] l.3293 +@x [9.186] l.3292 overflow("token"); @y overflow(_("token")); @z -@x [9.186] l.3297 +@x [9.186] l.3296 overflow("text"); @y overflow(_("text")); @z -@x [9.190] l.3352 +@x [9.190] l.3351 if (tok_ptr+6>tok_mem_end) overflow("token"); @y if (tok_ptr+6>tok_mem_end) overflow(_("token")); @z -@x [9.191] l.3358 +@x [9.191] l.3357 printf("\nIrreducible scrap sequence in section %d:",(int)section_count); @y printf(_("\nIrreducible scrap sequence in section %d:"),(int)section_count); @z -@x [9.192] l.3368 +@x [9.192] l.3367 printf("\nTracing after l. %d:\n",cur_line); mark_harmless(); @y printf(_("\nTracing after l. %d:\n"),cur_line); mark_harmless(); @z -@x [10.197] l.3500 +@x [10.197] l.3499 overflow("scrap/token/text"); @y overflow(_("scrap/token/text")); @z -@x [10.199] l.3585 +@x [10.199] l.3584 else err_print("! Double @@ should be used in strings"); @y else err_print(_("! Double @@ should be used in strings")); @z -@x [10.203] l.3668 +@x [10.203] l.3667 if (next_control!='|') err_print("! Missing '|' after C text"); @y if (next_control!='|') err_print(_("! Missing '|' after C text")); @z -@x [11.211] l.3809 +@x [11.211] l.3808 if (stack_ptr==stack_end) overflow("stack"); @y if (stack_ptr==stack_end) overflow(_("stack")); @z -@x [11.224] l.4132 +@x [11.224] l.4131 printf("%s","\n! Illegal control code in section name: <"); @y printf("%s",_("\n! Illegal control code in section name: <")); @z -@x [11.225] l.4147 +@x [11.225] l.4146 printf("%s","\n! C text in section name didn't end: <"); @y printf("%s",_("\n! C text in section name didn't end: <")); @z -@x [11.225] l.4159 +@x [11.225] l.4158 if (j>buffer+long_buf_size-3) overflow("buffer"); @y if (j>buffer+long_buf_size-3) overflow(_("buffer")); @z -@x [11.226] l.4167 +@x [11.226] l.4166 if (j>buffer+long_buf_size-4) overflow("buffer"); @y if (j>buffer+long_buf_size-4) overflow(_("buffer")); @z -@x [12.227] l.4182 +@x [12.227] l.4181 if (show_progress) printf("%s","\nWriting the output file..."); @y if (show_progress) printf("%s",_("\nWriting the output file...")); @z -@x [12.232] l.4269 +@x [12.232] l.4268 err_print("! TeX string should be in C text only"); break; @y err_print(_("! TeX string should be in C text only")); break; @z -@x [12.232] l.4275 +@x [12.232] l.4274 err_print("! You can't do that in TeX text"); break; @y err_print(_("! You can't do that in TeX text")); break; @z -@x [12.236] l.4347 +@x [12.236] l.4346 err_print("! Improper macro definition"); @y err_print(_("! Improper macro definition")); @z -@x [12.236] l.4360 +@x [12.236] l.4359 } @=/* otherwise fall through */@>@; default: err_print("! Improper macro definition"); break; @y @@ -496,62 +496,62 @@ if (show_progress) printf("%s",_("\nWriting the output file...")); default: err_print(_("! Improper macro definition")); break; @z -@x [12.237] l.4387 +@x [12.237] l.4386 if (scrap_ptr!=scrap_info+2) err_print("! Improper format definition"); @y if (scrap_ptr!=scrap_info+2) err_print(_("! Improper format definition")); @z -@x [12.240] l.4422 +@x [12.240] l.4421 err_print("! You need an = sign after the section name"); @y err_print(_("! You need an = sign after the section name")); @z -@x [12.241] l.4444 +@x [12.241] l.4443 err_print("! You can't do that in C text"); @y err_print(_("! You can't do that in C text")); @z -@x [13.247] l.4535 +@x [13.247] l.4534 if (show_progress) printf("%s","\nWriting the index..."); @y if (show_progress) printf("%s",_("\nWriting the index...")); @z -@x [13.247] l.4546 +@x [13.247] l.4545 fatal("! Cannot open index file ",idx_file_name); @y fatal(_("! Cannot open index file "),idx_file_name); @z -@x [13.247] l.4558 +@x [13.247] l.4557 fatal("! Cannot open section file ",scn_file_name); @y fatal(_("! Cannot open section file "),scn_file_name); @z -@x [13.247] l.4570 +@x [13.247] l.4569 fclose(active_file); @y fclose(active_file); active_file=tex_file=NULL; if (check_for_change) @<Update the result when it has changed@>@; @z -@x [13.247] l.4573 +@x [13.247] l.4572 printf("%s","Done."); @y printf("%s",_("Done.")); @z -@x [13.257] l.4725 +@x [13.257] l.4724 if (sort_ptr>=scrap_info_end) overflow("sorting"); @y if (sort_ptr>=scrap_info_end) overflow(_("sorting")); @z -@x [13.269] l.4862 +@x [13.269] l.4861 puts("\nMemory usage statistics:"); @.Memory usage statistics:@> printf("%td names (out of %ld)\n",@^system dependencies@> @@ -595,7 +595,7 @@ if (check_for_change) @<Update the result when it has changed@>@; (ptrdiff_t)(max_sort_ptr-scrap_info),(long)max_scraps); @z -@x [14.270] l.4884 +@x [14.270] l.4883 @** Index. @y @** Extensions to {\tentex CWEB}. The following sections introduce new or diff --git a/Build/source/texk/web2c/cwebdir/cweav-w32.ch b/Build/source/texk/web2c/cwebdir/cweav-w32.ch index 1bf1471e18e..2f9a2127ac5 100644 --- a/Build/source/texk/web2c/cwebdir/cweav-w32.ch +++ b/Build/source/texk/web2c/cwebdir/cweav-w32.ch @@ -2,9 +2,9 @@ This is the change file for CWEB's CWEAVE under Win32 (Contributed by Fabrice Popineau, February 2002) @x section 1 -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @y -@d banner "This is CWEAVE (Version 4.11win32)" +@d banner "This is CWEAVE (Version 4.12win32)" @z @x section 32 diff --git a/Build/source/texk/web2c/cwebdir/cweave.w b/Build/source/texk/web2c/cwebdir/cweave.w index 808d46cc7eb..015a3183b8e 100644 --- a/Build/source/texk/web2c/cwebdir/cweave.w +++ b/Build/source/texk/web2c/cwebdir/cweave.w @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 +% Version 4.12 --- August 2024 % Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth @@ -27,16 +27,15 @@ \def\dleft{[\![} \def\dright{]\!]} % double brackets \mathchardef\RA="3221 % right arrow \mathchardef\BA="3224 % double arrow -\def\({} % ) kludge for alphabetizing certain section names \def\TeXxstring{\\{\TEX/\_string}} \def\skipxTeX{\\{skip\_\TEX/}} \def\copyxTeX{\\{copy\_\TEX/}} -\def\title{CWEAVE (Version 4.11)} +\def\title{CWEAVE (Version 4.12)} \def\topofcontents{\null\vfill \centerline{\titlefont The {\ttitlefont CWEAVE} processor} \vskip 15pt - \centerline{(Version 4.11)} + \centerline{(Version 4.12)} \vfill} \def\botofcontents{\vfill \noindent @@ -67,7 +66,7 @@ Crusius, and others who have contributed improvements. The ``banner line'' defined here should be changed whenever \.{CWEAVE} is modified. -@d banner "This is CWEAVE (Version 4.11)" +@d banner "This is CWEAVE (Version 4.12)" @c @<Include files@>@/ @@ -94,7 +93,7 @@ char **av) /* argument values */ program=cweave; @<Set initial values@>@; common_init(); - @<Start \TEX/ output@>@; + @<Start \9{t}\TEX/ output@>@; if (show_banner) puts(banner); /* print a ``banner line'' */ @<Store all the reserved words@>@; phase_one(); /* read all the user's text and store the cross-references */ @@ -1077,9 +1076,9 @@ phase_one(void) { printf("*%d",(int)section_count); update_terminal(); /* print a progress report */ } - @<Store cross-references in the \TEX/ part of a section@>@; - @<Store cross-references in the definition part of a section@>@; - @<Store cross-references in the \CEE/ part of a section@>@; + @<Store cross-references in the \9{t}\TEX/ part of a section@>@; + @<Store cross-references in the \9{d}definition part of a section@>@; + @<Store cross-references in the \9{c}\CEE/ part of a section@>@; if (changed_section[section_count]) change_exists=true; } @@ -1156,7 +1155,7 @@ the identifiers in \CEE/ texts enclosed in \pb, or for control texts enclosed in \.{@@\^}$\,\ldots\,$\.{@@>} or \.{@@.}$\,\ldots\,$\.{@@>} or \.{@@:}$\,\ldots\,$\.{@@>}. -@<Store cross-references in the \T...@>= +@<Store cross-references in the \9{t}\T...@>= while (true) { switch (next_control=skip_TeX()) { case translit_code: err_print("! Use @@l in limbo only"); continue; @@ -1205,7 +1204,7 @@ static name_pointer res_wd_end; /* pointer to the first nonreserved identifier * @ When we get to the following code we have |next_control>=format_code|. -@<Store cross-references in the d...@>= +@<Store cross-references in the \9{d}d...@>= while (next_control<=definition) { /* |format_code| or |definition| */ if (next_control==definition) { xref_switch=def_flag; /* implied \.{@@!} */ @@ -1262,7 +1261,7 @@ else { @ Finally, when the \TEX/ and definition parts have been treated, we have |next_control>=begin_C|. -@<Store cross-references in the \CEE/...@>= +@<Store cross-references in the \9{c}\CEE/...@>= if (next_control<=section_name) { /* |begin_C| or |section_name| */ if (next_control==begin_C) section_xref_switch=0; else { @@ -1399,7 +1398,7 @@ beginning of phase two. We initialize the output variables in a slightly tricky way so that the first line of the output file will be `\.{\\input cwebmac}'. -@<Start \TEX/...@>= +@<Start \9{t}\TEX/...@>= out_ptr=out_buf+1; out_line=1; active_file=tex_file; tex_printf("\\input cwebma"); *out_ptr='c'; @@ -3421,10 +3420,10 @@ switch (next_control) { app_scrap(section_scrap,maybe_math);@+ app_scrap(exp,yes_math);@+break; case string: case constant: case verbatim: - @<Append a string or constant@>@;@+break; + @<Append a \9{s}string or constant@>@;@+break; case identifier: app_cur_id(true);@+break; case TeX_string: - @<Append a \TEX/ string, without forming a scrap@>@;@+break; + @<Append a \9{t}\TEX/ string, without forming a scrap@>@;@+break; case '/': case '.': app(next_control);@+app_scrap(binop,yes_math);@+break; case '<': app_str("\\langle");@+app_scrap(prelangle,yes_math);@+break; @@ -3546,7 +3545,7 @@ Many of the special characters in a string must be prefixed by `\.\\' so that \TEX/ will print them properly. @^special string characters@> -@<Append a string or...@>={@+ int count=-1; /* characters remaining before string break */ +@<Append a \9{s}string or...@>={@+ int count=-1; /* characters remaining before string break */ switch (next_control) { case constant: app_str("\\T{"@q}@>); break; @.\\T@> @@ -3612,7 +3611,7 @@ this bug is probably to enclose the \.{@@t...@@>} in \.{@@[...@@]} so that the \TEX/ string is treated as an expression. @^bug, known@> -@<Append a \TEX/ string, without forming a scrap@>= +@<Append a \9{t}\TEX/ string, without forming a scrap@>= app_str("\\hbox{"@q}@>); @^high-bit character handling@> while (id_first<id_loc) { @@ -4184,7 +4183,7 @@ if (show_progress) printf("%s","\nWriting the output file..."); section_count=0; format_visible=true; copy_limbo(); finish_line(); flush_buffer(out_buf,false,false); /* insert a blank line, it looks nice */ -while (!input_has_ended) @<Translate the current section@>@; +while (!input_has_ended) @<Translate the \9{c}current section@>@; } @ @<Predecl...@>=@+static void phase_two(void); @@ -4212,13 +4211,13 @@ static boolean format_visible; /* should the next format declaration be output? static boolean doing_format=false; /* are we outputting a format declaration? */ static boolean group_found=false; /* has a starred section occurred? */ -@ @<Translate the current section@>= { +@ @<Translate the \9{c}current section@>= { section_count++; @<Output the code for the beginning of a new section@>@; save_position(); - @<Translate the \TEX/ part of the current section@>@; - @<Translate the definition part of the current section@>@; - @<Translate the \CEE/ part of the current section@>@; + @<Translate the \9{t}\TEX/ part of the current section@>@; + @<Translate the \9{d}definition part of the current section@>@; + @<Translate the \9{c}\CEE/ part of the current section@>@; @<Show cross-references to this section@>@; @<Output the code for the end of a section@>@; } @@ -4258,7 +4257,7 @@ out('{'); out_section(section_count); out('}'); @ In the \TEX/ part of a section, we simply copy the source text, except that index entries are not copied and \CEE/ text within \pb\ is translated. -@<Translate the \T...@>= do +@<Translate the \9{t}\T...@>= do switch (next_control=copy_TeX()) { case '|': init_stack(); output_C(); break; case '@@': out('@@'); break; @@ -4280,12 +4279,12 @@ while (next_control<format_code); @ When we get to the following code we have |next_control>=format_code|, and the token memory is in its initial empty state. -@<Translate the d...@>= +@<Translate the \9{d}d...@>= space_checked=false; while (next_control<=definition) { /* |format_code| or |definition| */ init_stack(); - if (next_control==definition) @<Start a macro definition@>@; - else @<Start a format definition@>@; + if (next_control==definition) @<Start \9{a}a macro definition@>@; + else @<Start \9{a}a format definition@>@; outer_parse(); finish_C(format_visible); format_visible=true; doing_format=false; } @@ -4338,7 +4337,7 @@ if the identifier is not followed by `\.(' at all, the replacement text starts immediately after the identifier. In the former case, it starts after we scan the matching `\.)'. -@<Start a macro...@>= { +@<Start \9{a}a macro...@>= { if (save_line!=out_line || save_place!=out_ptr || space_checked) app(backup); if(!space_checked){emit_space_if_needed();save_position();} app_str("\\D"); /* this will produce `\#\&{define }' */ @@ -4367,7 +4366,7 @@ it starts after we scan the matching `\.)'. } } -@ @<Start a format...@>= { +@ @<Start \9{a}a format...@>= { doing_format=true; if(*(loc-1)=='s' || *(loc-1)=='S') format_visible=false; if(!space_checked){emit_space_if_needed();save_position();} @@ -4395,7 +4394,7 @@ point to the current section name, if it has a name. @<Private...@>= static name_pointer this_section; /* the current section name, or zero */ -@ @<Translate the \CEE/...@>= +@ @<Translate the \9{c}\CEE/...@>= this_section=name_dir; if (next_control<=section_name) { emit_space_if_needed(); init_stack(); diff --git a/Build/source/texk/web2c/cwebdir/cwebacromac.tex b/Build/source/texk/web2c/cwebdir/cwebacromac.tex index f8462d06a37..1743ddd95d6 100644 --- a/Build/source/texk/web2c/cwebdir/cwebacromac.tex +++ b/Build/source/texk/web2c/cwebdir/cwebacromac.tex @@ -1,6 +1,8 @@ % hypertext macros for CWEB listings (in addition to cwebmac.tex) -% Version 2.0 --- May 2024 +% Used in cwebmac.tex, pdfctwimac.tex, pdfproofmac.tex +% Version 3.0 --- August 2024 \ifx\ifacrohint\undefined\endinput\fi % these are not standalone macros + \ifluatex % LuaTeX is almost like pdfTeX \protected\def\pdfliteral {\pdfextension literal} \protected\def\pdfstartlink {\pdfextension startlink\space} @@ -15,7 +17,16 @@ \let\pdfoutput=\outputmode \fi % end of LuaTeX->pdfTeX redirection macros -% Used in cwebmac.tex, pdfctwimac.tex, pdfproofmac.tex +\def\pdfURL#1#2{\ifpdf \ifpdflua\pdfannotlink height \theheight depth \thedepth + attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A + << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69 + \else {\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}% + \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight + depth \thedepth << /Border [0 0 0] /Type /Annot /Subtype /Link /A + << /S /URI /URI (#2) >> >>}\box0\relax}\fi + \else #1 ({\tt#2})\fi} +{\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL +{\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL \def\tocpages{1} % Most programs have one-page Table-of-Contents \def\startpdf{\def\pagemode{/PageMode /UseOutlines} \ifnum\contentspagenumber=0 \advance \pageno by -\tocpages @@ -24,9 +35,52 @@ \else \twodigits=\pageno \pageno=\contentspagenumber \advance \pageno by \tocpages \advance \twodigits by -\pageno \def\pagelabels{/PageLabels << /Nums [ 0 << /S/D/St \the\pageno >> - \the\twodigits << /S/D/St \contentspagenumber >> ] >>} \fi + \the\twodigits << /S/D/St \contentspagenumber >> ] >>} + \fi \ifpdflua\pdfcatalog{\pagemode\space\pagelabels} - \else \special{pdf: docview << \pagemode\space\pagelabels >>}\fi} + \else\special{pdf: docview << \pagemode\space\pagelabels >>}\fi} +\ifacrohint\def\digits{\pdflink{\the\countA}\scan} +\else\def\digits{{\the\countA}\scan}\fi +\def\scan#1{\begingroup + \ifx!#1% exit on exclamation point + \else\ifx,#1,\space\aftergroup\scan % insert ,\space}\scan ... + \else\ifx\[#1\aftergroup#1% insert }\[... + \else\ifx\*#1\aftergroup\lapstar\aftergroup\scan % insert }\lapstar\scan ... + \else\ifnum`#1>`9#1\aftergroup\scan % insert #1}\scan ... + \else\ifnum`#1<`0#1\aftergroup\scan % insert #1}\scan ... + \else + \afterassignment\digits \aftergroup\countA + \aftergroup#1% insert }\countA=#1...\digits ... + \fi\fi\fi\fi\fi \fi + \endgroup} + +% Local variables; there are a few externals like '\toksA' and '\countA' +\newtoks\toksB \newtoks\toksC \newtoks\toksD \newtoks\toksF \newtoks\sanitizer +\newif\iftokprocessed \newif\ifTnum \newif\ifinstr \newif\ifhavenum + +\ifacrohint % Common macros for \ifacro and \ifhint to create \pdflink's +\def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA} +\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}% + \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\let\space\empty + \toksC={}\toksD={}}\makenote}\maketoks}} +\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} +\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}\toksA={#2}% + \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi} +\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|% + \let\next=\maketoks % continue processing by default + \ifnum\countB>`9 \countB=0 \fi + \ifnum\countB<`0 + \ifhavenum\makenote\fi + \ifx\first.\let\next=\maketoksdone % fullstop + \else \addtokens\toksB{\the\toksD} \ifx\first,\addtokens\toksB{\space}\fi + \fi + \else \addtokens\toksC{\the\toksD}\global\havenumtrue + \fi + \next} +\def\makenote{\addtokens\toksB + {\noexpand\pdflink{\the\toksC}}\toksC={}\global\havenumfalse} +\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} +\fi % End of common pdf/hint macros \ifacro % The following are general pdf macros \def\thewidth{\the\wd0 \space} @@ -34,13 +88,14 @@ \def\thedepth{\the\dp\strutbox\space} \ifpdflua \ifx\pdfannotlink\undefined\let\pdfannotlink\pdfstartlink\fi% for pdfTeX 0.14 - \def\pdflink#1#2{\hbox{\pdfannotlink height \theheight depth \thedepth + \def\pdflink#1{\hbox{\pdfannotlink height \theheight depth \thedepth attr{/Border [0 0 0]} goto num #1 \Blue #1\Black\pdfendlink}} % changed 3.69 -\else\def\pdflink#1#2{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}% +\else\def\pdflink#1{\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}% \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight - depth \thedepth << /Type /Annot /Subtype /Link - /Border [0 0 0] /A << /S /GoTo /D (#2) >> >>}\box0\relax}\fi -\def\sanitizecommand#1#2{\addtokens\usersanitizer + depth \thedepth << /Type /Annot /Subtype /Link /Border [0 0 0] + /A << /S /GoTo /D (\romannumeral#1) >> >>}\box0\relax} +\fi +\def\sanitizecommand#1#2{\addtokens\sanitizer {\noexpand\dosanitizecommand\noexpand#1{#2}}} \def\dosanitizecommand#1#2{\ifx\nxt#1\addF{#2}\fi} @@ -49,9 +104,11 @@ \catcode`\[=12 \catcode`\]=12 \catcode`\{=1 \catcode`\}=2 \catcode`\~=12 \def\tildechar{~} \catcode`\~=13 \catcode`\|=0 |catcode`|\=12 |def|bschar{\} |catcode`|\=0 \catcode`\|=12 -\def\makeoutlinetoks{\Tnumfalse\afterassignment\makeolproctok\let\nxt= } +\def\makeoutlinetoks{\toksF={}\Tnumfalse + \afterassignment\makeolproctok\let\nxt= } \def\makeolnexttok{\afterassignment\makeolproctok\let\nxt= } \def\makeolgobbletok{\afterassignment\makeolnexttok\let\nxt= } +\def\makeolgobbletokk{\afterassignment\makeolgobbletok\let\nxt= } \def\addF#1{\addtokens\toksF{#1}\tokprocessedtrue} % now comes a routine to "sanitize" section names, for pdf outlines \def\makeolproctok{\tokprocessedfalse @@ -62,9 +119,8 @@ \else\ifx^\nxt \addF^\else\ifx_\nxt \addF_% sanitize ^ and _ \else\ifx\nxt\spacechar \addF\space \else\if\noexpand\nxt\relax % we have a control sequence; is it one we know? - \ifx\nxt~\addF\space - \else\ifx\nxt\onespace\addF\space - \else\the\usersanitizer + \ifx\nxt~\addF\space \else\ifx\nxt\onespace\addF\space + \else\the\sanitizer \iftokprocessed\else\makeolproctokctli \iftokprocessed\else\makeolproctokctlii \iftokprocessed\else\makeolproctokctliii % if not recognised, skip it @@ -77,8 +133,7 @@ \else\expandafter\makeolproctokchar\meaning\nxt \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi \fi\fi\fi - \next -} + \next} \def\makeolproctokchar#1 #2 #3{\addF{#3}} \def\makeolproctokctli{% \ifx\nxt\CEE\addF{C}\let\next\makeolgobbletok % \CEE/ @@ -86,8 +141,9 @@ \else\ifx\nxt\TEX\addF{TeX}\let\next\makeolgobbletok % \TEX/ \else\ifx\nxt\TeX\addF{TeX}\else\ifx\nxt\LaTeX\addF{LaTeX}% \else\ifx\nxt\CPLUSPLUS\addF{C++}\let\next\makeolgobbletok % \CPLUSPLUS/ - \else\ifx\nxt\Cee\addF{C}% + \else\ifx\nxt\Cee\addF{C}\else\ifx\nxt\dots\addF{...\space}% \else\ifx\nxt\PB \let\next\makeolgobbletok \tokprocessedtrue % \PB{...} + \else\ifx\nxt\9 \expandafter\nxt\relax \let\next\makeolgobbletokk % \9{#1} \else\ifx\nxt\.\tokprocessedtrue\instrtrue % \.{...} % skip \| \else\ifx\nxt\\\ifinstr\addF{\bschar\bschar}\else\tokprocessedtrue\fi @@ -102,7 +158,7 @@ \else\ifx\nxt\PP\addF{++}\else\ifx\nxt\MM\addF{--}% \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi \fi\fi\fi\fi\fi - \fi\fi + \fi\fi\fi\fi } \def\makeolproctokctlii{% \ifx\nxt\MG\addF{->}\else\ifx\nxt\GG\addF{>>}% @@ -141,48 +197,19 @@ \fi\fi\fi\fi\fi } \def\outlinedone{\edef\outlinest{\global\noexpand\toksE={\the\toksF}}% - \outlinest\let\outlinedone=\relax} + \outlinest\let\outlinedone=\relax\outlinedone} \fi % End of pdf macros \ifhint -% The following uses HINT primitives to implement: +% The following top level macros use HINT primitives to implement: % \HINThome to mark the position of the home page % \HINTlabel to attach a label to the top of a section % \HINTlink to link a section number to the section label % \HINTcontents to produce an 'outline' item -% These are the top level HINT macros that are used in this file -% No HINT primitives are used directly. \def\HINThome{\HINTdest name {HINT.home}% \HINToutline goto name {HINT.home} depth 1 {Table of Contents}} \def\HINTlabel{{\let\*=\empty\HINTdest num \secstar top}} -\def\HINTlink#1#2{\HINTstartlink goto num #1 {#1}\HINTendlink}% #2 not used ??? +\def\HINTlink#1{\HINTstartlink goto num #1 {#1}\HINTendlink} \def\HINTcontents#1#2#3{\HINToutline goto num #3 depth #2 {#1}} \let\pdflink=\HINTlink \fi % End of HINT macros - -% Common macros for \ifacro and \ifhint -\ifacrohint -\def\pdfnote#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA} -\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}% - \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\toksD={} - \toksC={}\let\space\empty}\makenote}\maketoks}} -\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} -\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}% - \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}} -\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|% - \ifnum\countB>`9 \countB=0 \fi - \ifnum\countB<`0 - \ifnum0=\countC\else\makenote\fi - \ifx\first.\let\next=\maketoksdone\else - \let\next=\maketoks - \addtokens\toksB{\the\toksD} - \ifx\first,\addtokens\toksB{\space}\fi - \fi - \else \addtokens\toksC{\the\toksD}\global\countC=1\let\next=\maketoks - \fi - \next -} -\def\makenote{\addtokens\toksB - {\noexpand\pdflink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0} -\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} -\fi % End of common macros diff --git a/Build/source/texk/web2c/cwebdir/cwebmac.tex b/Build/source/texk/web2c/cwebdir/cwebmac.tex index 8685608528b..53454300a9a 100644 --- a/Build/source/texk/web2c/cwebdir/cwebmac.tex +++ b/Build/source/texk/web2c/cwebdir/cwebmac.tex @@ -1,12 +1,9 @@ % standard macros for CWEB listings (in addition to plain.tex) -% Version 4.12 --- May 2024 +% Version 4.12 --- August 2024 \ifx\renewenvironment\undefined\else\endinput\fi % LaTeX will use other macros \xdef\fmtversion{\fmtversion+CWEB4.12} \chardef\cwebversion=4 \chardef\cwebrevision=12 -\let\:=\. % preserve a way to get the dot accent - % (all other accents will still work as usual) - \parskip 0pt % no stretch between paragraphs \parindent 1em % for paragraphs and for the first line of C text @@ -28,9 +25,10 @@ \def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} \def\Cee{\CEE/} % for backward compatibility \def\Cpp{\CPLUSPLUS/} % for backward compatibility -\def\9#1{} - % with this definition of \9 you can say @:sort key}{TeX code@> - % to alphabetize an index entry by the sort key but format with the TeX code +\def\9#1{} % with this definition of \9 you can say @:sort key}{TeX code@> + % to alphabetize an index entry by the sort key but format with the TeX code +\let\:=\. % preserve a way to get the dot accent + % (all other accents will still work as usual) \def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers \def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way @@ -113,14 +111,10 @@ \let\yskip=\smallskip \def\?{\mathrel?} \def\,{\relax\ifmmode\mskip\thinmuskip\else\thinspace\fi} -\def\note#1#2.{\Y\noindent{\hangindent2em% - \baselineskip10pt\eightrm#1~\ifacrohint{\pdfnote#2.}\else#2\fi.\par}} -\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD -\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer +\newtoks\toksA \newtoks\toksE \newcount\countA \countA=0 \newcount\countB \countB=0 -\newcount\countC \countC=0 \newcount\countD \countD=0 -\newif\iftokprocessed \newif\ifTnum \newif\ifinstr +\newcount\countNOS \countNOS=0 {\def\\{\global\let\spacechar= }\\ } % Here we decide the output format, depending on the TeX engine in use: @@ -137,17 +131,7 @@ \def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too \def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}} \fi -\ifacrohint \input cwebacromac \fi % load hypertext macros -\def\pdfURL#1#2{\ifpdf \ifpdflua\pdfannotlink height \theheight depth \thedepth - attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A - << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69 - \else {\setbox0=\hbox{\special{pdf: bc [ \pdflinkcolor ]}{#1}% - \special{pdf: ec}}\special{pdf: ann width \thewidth height \theheight - depth \thedepth << /Border [0 0 0] /Type /Annot /Subtype /Link /A - << /S /URI /URI (#2) >> >>}\box0\relax}\fi - \else #1 ({\tt#2})\fi} -{\catcode`\~=12 \gdef\TILDE/{~}} % ~ in a URL -{\catcode`\_=12 \gdef\UNDER/{_}} % _ in a URL +\input cwebacromac % load hypertext macros \def\lapstar{\rlap{*}} \def\stsec{\rightskip=0pt % get out of C mode (cf. \B) @@ -160,6 +144,8 @@ [ @thispage /FitH @ypos ]}\fi}}\fi} \let\startsection=\stsec \def\defin#1{\global\advance\ind by 2 \1\&{#1 }} % begin `define' or `format' +\def\note#1#2.{\Y\noindent{\hangindent2em\baselineskip10pt% + \eightrm#1~\ifacrohint{\pdfnote#2.}\else#2\fi.\par}} \def\A{\note{See also section}} % xref for doubly defined section name \def\As{\note{See also sections}} % xref for multiply defined section name \def\B{\rightskip=0pt plus 100pt minus 10pt % go into C mode @@ -187,7 +173,7 @@ \outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section \vskip\intersecskip\startsection\ignorespaces} \outer\def\N#1#2#3.{% beginning of starred section - \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi + \ifacro{\makeoutlinetoks#3\outlinedone}\fi \gdepth=#1\gtitle={#3}\MN{#2}% \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi @@ -241,15 +227,13 @@ \def\oct{\hbox{$^\circ$\kern-.1em\it\aftergroup\?\aftergroup}} % CWEB style \def\hex{\hbox{$^{\scriptscriptstyle\#}$\tt\aftergroup}} % CWEB style \def\bin{\hbox{$^{\scriptscriptstyle b}$\tt\aftergroup}} % new in CWEB 4.3 -\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule - \hbox{\strut\kern2pt\.{#1}\kern2pt}} - \hrule}\vrule\kern2pt}} % verbatim string +\def\vb#1{\leavevmode\hbox{\kern2pt\vrule\vtop{\vbox{\hrule\hbox{\strut + \kern2pt\.{#1}\kern2pt}}\hrule}\vrule\kern2pt}} % verbatim string \def\p#1{\cdot 2^{#1}} % power of two (hex exponent) \def\onmaybe{\let\ifon=\maybe} \let\maybe=\iftrue -\newif\ifon \newif\iftitle \newif\ifpagesaved +\newif\ifon \newif\iftitle \newif\ifpagesaved \newif\ifheader -\newif\ifheader \def\lheader{\headertrue\mainfont\the\pageno\eightrm\qquad\grouptitle \hfill\title\qquad\mainfont\topsecno} % top line on left-hand pages \def\rheader{\headertrue\mainfont\topsecno\eightrm\qquad\title\hfill @@ -318,9 +302,7 @@ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height \hsize=.5\pagewidth \advance\hsize by -10pt % column width for the index (20pt between cols) - \ifhint\else - \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines - \fi + \ifhint\else\parfillskip 0pt plus .6\hsize\fi % avoid almost empty lines \def\lr{L} % this tells whether the left or right column is next \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} \else\normaloutput{\vbox to\pageheight{\box\sbox\vss @@ -329,9 +311,9 @@ \message{Index:} \parskip 0pt plus .5pt \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em - \ifacrohint\pdfnote##2.\else##2\fi.} % index entry - \def\[##1]{$\underline{##1}$} % underlined index item - \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \scan##2!.} % index entry + \def\[##1]{$\underline{\scan##1!}$\scan} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \hyphenpenalty 10000 \parindent0pt \readindex} \def\fin{\par\vfill\eject % this is done when we are ending the index @@ -354,23 +336,21 @@ \ifacro \def\outsecname{Names of the sections} \let\Xpdf\X % \ifpdflua \makebookmarks \pdfdest name {NOS} fitb % in versions < 3.68 \ifpdflua \pdfdest name {NOS} fith % changed in version 3.69 - \pdfoutline goto name {NOS} count -\the\countD {\outsecname} - \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% - {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% - \pdfoutline goto num \the\toksA \expandafter{\the\toksE}} + \pdfoutline goto name {NOS} count -\the\countNOS {\outsecname} \else \special{pdf: outline -1 << /Title (\outsecname) - /Dest [ @thispage /FitH @ypos ] >>} - \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% - {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% - \special{pdf: outline 0 << /Title (\the\toksE) - /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}} - \fi\fi + /Dest [ @thispage /FitH @ypos ] >>}\fi + \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% + {\makeoutlinetoks##2\outlinedone}% + \ifpdflua \pdfoutline goto num \the\toksA \expandafter{\the\toksE} + \else \special{pdf: outline 0 << /Title (\the\toksE) + /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}\fi} + \fi % \ifacro \readsections} \def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax} \def\countsections{\message{Number of named sections:} - {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax} + {\def\I{\global\advance\countNOS by 1}\def\X##1\X{\relax} \def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax} - \readsections\relax}\message{\the\countD}} + \readsections\relax}\message{\the\countNOS}} \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% \else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex \def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1 @@ -396,9 +376,9 @@ \line{\consetup{#2}#1 \rm\leaders\hbox to .5em{.\hfil}\hfil \ \ifhint - \HINTlink{#3}{\romannumeral#3}% No page numbers in HINT + \HINTlink{#3}% No page numbers in HINT \HINTcontents{#1}{#2}{#3}% - \else\ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi + \else\ifacro\pdflink{#3}\else#3\fi \hbox to3em{\hss#4}\fi}} \def\consetup#1{\ifcase#1 \bf % depth -1 (@**) \or % depth 0 (@*) diff --git a/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch b/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch index 9e7bceb20e2..ad478b81019 100644 --- a/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch +++ b/Build/source/texk/web2c/cwebdir/cwebman-w2c.ch @@ -94,17 +94,17 @@ @z @x l.34 -\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.11)} +\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.12)} @y \def\Kpathsea/{{\mc KPATHSEA\spacefactor1000}} -\def\runninghead{{\ninett CWEB} USER MANUAL (Version 4.11 [\TeX~Live])} +\def\runninghead{{\ninett CWEB} USER MANUAL (Version 4.12 [\TeX~Live])} @z @x l.48 -\vskip 18pt\centerline{(Version 4.11 --- December 2023)} +\vskip 18pt\centerline{(Version 4.12 --- August 2024)} @y -\vskip 18pt\centerline{(Version 4.11 --- December 2023)% -\footnote*{This document describes the extended \.{CWEB} (Version 4.11 [\TeX~Live]).}} +\vskip 18pt\centerline{(Version 4.12 --- August 2024)% +\footnote*{This document describes the extended \.{CWEB} (Version 4.12 [\TeX~Live]).}} @z @x l.79 @@ -347,9 +347,9 @@ And here's what the same excerpt looks like when typeset. @z @x l.1613 - \.{ { }\\vskip 15pt \\centerline\{(Version 4.11)\}{ }\\vfill\}}\cr}$$ + \.{ { }\\vskip 15pt \\centerline\{(Version 4.12)\}{ }\\vfill\}}\cr}$$ @y - \.{ { }\\vskip 15pt \\centerline\{(Version 4.11)\}{ }\\vfill\}}\cr}$$ + \.{ { }\\vskip 15pt \\centerline\{(Version 4.12)\}{ }\\vfill\}}\cr}$$ @z @x l.1728 diff --git a/Build/source/texk/web2c/cwebdir/cwebman.tex b/Build/source/texk/web2c/cwebdir/cwebman.tex index c4bc660df3b..25654a53172 100644 --- a/Build/source/texk/web2c/cwebdir/cwebman.tex +++ b/Build/source/texk/web2c/cwebdir/cwebman.tex @@ -31,7 +31,7 @@ \def\lheader{\mainfont\the\pageno\hfill\sc\runninghead\hfill} \def\rheader{\hfill\sc\runninghead\hfill\mainfont\the\pageno} -\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.11)} +\def\runninghead{{\ninett CWEB} USER MANUAL (VERSION 4.12)} % This verbatim mode assumes that ! marks are !! in the text being copied. \def\verbatim{\begingroup @@ -45,7 +45,7 @@ \null\vfill \centerline{\titlefont The {\ttitlefont CWEB} System of Structured Documentation} -\vskip 18pt\centerline{(Version 4.11 --- December 2023)} +\vskip 18pt\centerline{(Version 4.12 --- August 2024)} \vskip 24pt \centerline{\authorfont Donald E. Knuth and Silvio Levy} \vfill @@ -1527,9 +1527,9 @@ features needed by the output of \.{CWEAVE}. \printmacs{\empty} \vskip6pt \baselineskip12pt -\noindent And here comes the file that extends \.{cwebmac.tex} in order to -support the ``hypertext'' features needed by the enhanced \TEX/ engines when -processing the output of \.{CWEAVE}. +\noindent And here comes the file \.{cwebacromac.tex} that extends +\.{cwebmac.tex} in order to support the ``hypertext'' features needed +by the enhanced \TEX/ engines when processing the output of \.{CWEAVE}. \vskip6pt \baselineskip9pt \printmacs{acro} @@ -1610,7 +1610,7 @@ $$\lpile{\.{\\def\\topofcontents\{\\null\\vfill}\cr \.{ { }\\titlefalse \% include headline on the contents page}\cr \.{ { }\\def\\rheader\{\\mainfont The \{\\tt CWEAVE\}{ }processor\\hfil\}}\cr \.{ { }\\centerline\{\\titlefont The \{\\ttitlefont CWEAVE\}{ }processor\}}\cr - \.{ { }\\vskip 15pt \\centerline\{(Version 4.11)\}{ }\\vfill\}}\cr}$$ + \.{ { }\\vskip 15pt \\centerline\{(Version 4.12)\}{ }\\vfill\}}\cr}$$ Redefining \.{\\rheader}, which is the headline for right-hand pages, suffices in this case to put the desired information at the top of the contents page. diff --git a/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot b/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot index 19c348da56a..9ba80115a93 100644 --- a/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot +++ b/Build/source/texk/web2c/cwebdir/po/cweb-tl.pot @@ -6,9 +6,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: CWEBbin 2023\n" +"Project-Id-Version: CWEBbin 2024\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-02 13:08+0100\n" +"POT-Creation-Date: 2024-08-12 13:10+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Language-Team: LANGUAGE <LL@li.org>\n" @@ -30,15 +30,15 @@ msgid "%s: Need one to three file arguments.\n" msgstr "" #: ctang-texlive.ch:39 -msgid "This is CTANGLE, Version 4.11" +msgid "This is CTANGLE, Version 4.12" msgstr "" #: ctwill-texlive.ch:39 -msgid "This is CTWILL, Version 4.11" +msgid "This is CTWILL, Version 4.12" msgstr "" #: cweav-texlive.ch:39 -msgid "This is CWEAVE, Version 4.11" +msgid "This is CWEAVE, Version 4.12" msgstr "" #: comm-texlive.ch:512 diff --git a/Build/source/texk/web2c/cwebdir/po/cweb.pot b/Build/source/texk/web2c/cwebdir/po/cweb.pot index 8db3c5a3168..f451101937c 100644 --- a/Build/source/texk/web2c/cwebdir/po/cweb.pot +++ b/Build/source/texk/web2c/cwebdir/po/cweb.pot @@ -523,15 +523,15 @@ msgid "Sorting:" msgstr "" #: ctang-i18n.ch:17 -msgid "This is CTANGLE (Version 4.11 [CWEBbin 2023])" +msgid "This is CTANGLE (Version 4.12 [CWEBbin 2024])" msgstr "" #: cweav-twill.ch:61 -msgid "This is CTWILL (Version 4.11 [CWEBbin 2023])" +msgid "This is CTWILL (Version 4.12 [CWEBbin 2024])" msgstr "" #: cweav-i18n.ch:17 cweav-twill.ch:42 -msgid "This is CWEAVE (Version 4.11 [CWEBbin 2023])" +msgid "This is CWEAVE (Version 4.12 [CWEBbin 2024])" msgstr "" #: cweav-i18n.ch:301 cweav-i18n.ch:309 diff --git a/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po b/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po index 7d875bdd177..b1412747cbf 100644 --- a/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po +++ b/Build/source/texk/web2c/cwebdir/po/de/cweb-tl.po @@ -1,14 +1,14 @@ # International version of CWEB (CWEBbin, TeXLive). -# Copyright (C) 2023 Andreas Scherer et al. +# Copyright (C) 2024 Andreas Scherer et al. # This file is distributed under the same license as the CWEB package. # Andreas Scherer <https://ascherer.github.io>. # msgid "" msgstr "" -"Project-Id-Version: CWEBbin 2023\n" +"Project-Id-Version: CWEBbin 2024\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-02 13:10+0100\n" -"PO-Revision-Date: 2023-12-02 13:10+0100\n" +"POT-Creation-Date: 2024-08-12 13:10+0200\n" +"PO-Revision-Date: 2024-08-12 13:10+0200\n" "Last-Translator: Andreas Scherer <andreas_github@freenet.de>\n" "Language-Team: German\n" "Language: de\n" @@ -33,16 +33,16 @@ msgid "%s: Need one to three file arguments.\n" msgstr "%s: Benötige ein bis drei Dateiargumente.\n" #: ctang-texlive.ch:39 -msgid "This is CTANGLE, Version 4.11" -msgstr "Dies ist CTANGLE, Version 4.11" +msgid "This is CTANGLE, Version 4.12" +msgstr "Dies ist CTANGLE, Version 4.12" #: ctwill-texlive.ch:39 -msgid "This is CTWILL, Version 4.11" -msgstr "Dies ist CTWILL, Version 4.11" +msgid "This is CTWILL, Version 4.12" +msgstr "Dies ist CTWILL, Version 4.12" #: cweav-texlive.ch:39 -msgid "This is CWEAVE, Version 4.11" -msgstr "Dies ist CWEAVE, Version 4.11" +msgid "This is CWEAVE, Version 4.12" +msgstr "Dies ist CWEAVE, Version 4.12" #: comm-texlive.ch:512 #, c-format diff --git a/Build/source/texk/web2c/cwebdir/po/de/cweb.po b/Build/source/texk/web2c/cwebdir/po/de/cweb.po index f93952f9a06..0b459e1b01b 100644 --- a/Build/source/texk/web2c/cwebdir/po/de/cweb.po +++ b/Build/source/texk/web2c/cwebdir/po/de/cweb.po @@ -603,16 +603,16 @@ msgid "Sorting:" msgstr "Sortieren:" #: ctang-i18n.ch:17 -msgid "This is CTANGLE (Version 4.11 [CWEBbin 2023])" -msgstr "Dies ist CTANGLE (Version 4.11 [CWEBbin 2023])" +msgid "This is CTANGLE (Version 4.12 [CWEBbin 2024])" +msgstr "Dies ist CTANGLE (Version 4.12 [CWEBbin 2024])" #: cweav-twill.ch:61 -msgid "This is CTWILL (Version 4.11 [CWEBbin 2023])" -msgstr "Dies ist CTWILL (Version 4.11 [CWEBbin 2023])" +msgid "This is CTWILL (Version 4.12 [CWEBbin 2024])" +msgstr "Dies ist CTWILL (Version 4.12 [CWEBbin 2024])" #: cweav-i18n.ch:17 cweav-twill.ch:42 -msgid "This is CWEAVE (Version 4.11 [CWEBbin 2023])" -msgstr "Dies ist CWEAVE (Version 4.11 [CWEBbin 2023])" +msgid "This is CWEAVE (Version 4.12 [CWEBbin 2024])" +msgstr "Dies ist CWEAVE (Version 4.12 [CWEBbin 2024])" #: cweav-i18n.ch:301 cweav-i18n.ch:309 msgid "buffer" diff --git a/Build/source/texk/web2c/cwebdir/po/it/cweb.po b/Build/source/texk/web2c/cwebdir/po/it/cweb.po index a171f020d28..2cf95a994d3 100644 --- a/Build/source/texk/web2c/cwebdir/po/it/cweb.po +++ b/Build/source/texk/web2c/cwebdir/po/it/cweb.po @@ -1,14 +1,14 @@ # International version of CWEB (CWEBbin, TeXLive). -# Copyright (C) 2023 Andreas Scherer et al. +# Copyright (C) 2024 Andreas Scherer et al. # This file is distributed under the same license as the CWEB package. # Andreas Scherer <https://ascherer/github.io>. # msgid "" msgstr "" -"Project-Id-Version: CWEBbin 2023\n" +"Project-Id-Version: CWEBbin 2024\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2023-12-02 13:10+0100\n" -"PO-Revision-Date: 2023-12-02 13:10+0100\n" +"POT-Creation-Date: 2024-08-12 13:10+0200\n" +"PO-Revision-Date: 2024-08-12 13:10+0200\n" "Last-Translator: Andreas Scherer <andreas_github@freenet.de>\n" "Language-Team: Italian\n" "Language: it\n" @@ -598,16 +598,16 @@ msgid "Sorting:" msgstr "Ordinamento:" #: ctang-i18n.ch:17 -msgid "This is CTANGLE (Version 4.11 [CWEBbin 2023])" -msgstr "Questo è CTANGLE (Versione 4.11 [CWEBbin 2023])" +msgid "This is CTANGLE (Version 4.12 [CWEBbin 2024])" +msgstr "Questo è CTANGLE (Versione 4.12 [CWEBbin 2024])" #: cweav-i18n.ch:17 -msgid "This is CWEAVE (Version 4.11 [CWEBbin 2023])" -msgstr "Questo è CWEAVE (Versione 4.11 [CWEBbin 2023])" +msgid "This is CWEAVE (Version 4.12 [CWEBbin 2024])" +msgstr "Questo è CWEAVE (Versione 4.12 [CWEBbin 2024])" #: cweav-twill.ch:68 -msgid "This is CTWILL (Version 4.11 [CWEBbin 2023])" -msgstr "Questo è CTWILL (Versione 4.11 [CWEBbin 2023])" +msgid "This is CTWILL (Version 4.12 [CWEBbin 2024])" +msgstr "Questo è CTWILL (Versione 4.12 [CWEBbin 2024])" #: cweav-i18n.ch:301 cweav-i18n.ch:309 msgid "buffer" diff --git a/Build/source/texk/web2c/cwebdir/prod-cweave.w b/Build/source/texk/web2c/cwebdir/prod-cweave.w index 6c75760fa2f..e7939716b72 100644 --- a/Build/source/texk/web2c/cwebdir/prod-cweave.w +++ b/Build/source/texk/web2c/cwebdir/prod-cweave.w @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 +% Version 4.12 --- August 2024 % @ Here is a table of all the productions. Each production that combines two or more consecutive scraps implicitly inserts a {\tt \$} diff --git a/Build/source/texk/web2c/cwebdir/prod-twill.w b/Build/source/texk/web2c/cwebdir/prod-twill.w index 63ebdb4893d..c1b67aedc90 100644 --- a/Build/source/texk/web2c/cwebdir/prod-twill.w +++ b/Build/source/texk/web2c/cwebdir/prod-twill.w @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 +% Version 4.12 --- August 2024 % @ Here is a table of all the productions. Each production that combines two or more consecutive scraps implicitly inserts a {\tt \$} diff --git a/Build/source/texk/web2c/cwebdir/prod.w b/Build/source/texk/web2c/cwebdir/prod.w index b0da3ddce5f..851c5ffe234 100644 --- a/Build/source/texk/web2c/cwebdir/prod.w +++ b/Build/source/texk/web2c/cwebdir/prod.w @@ -2,7 +2,7 @@ % This program by Silvio Levy and Donald E. Knuth % is based on a program by Knuth. % It is distributed WITHOUT ANY WARRANTY, express or implied. -% Version 4.11 --- December 2023 +% Version 4.12 --- August 2024 % @ Here is a table of all the productions. Each production that combines two or more consecutive scraps implicitly inserts a {\tt \$} diff --git a/Build/source/texk/web2c/cwebdir/refsort.ch b/Build/source/texk/web2c/cwebdir/refsort.ch index 67175b030f7..2ebedfca696 100644 --- a/Build/source/texk/web2c/cwebdir/refsort.ch +++ b/Build/source/texk/web2c/cwebdir/refsort.ch @@ -45,12 +45,24 @@ int main() item *x, **y; @z +@x [4] l.83 + @<Copy |buf| to item |x|@>; +@y + @<Copy \9{b}|buf| to item |x|@>; +@z + @x [5] l.89 variable not used { register int k; @y { @z +@x [6] l.99 +@<Copy |buf|...@>= +@y +@<Copy \9{b}|buf|...@>= +@z + @x [9] l.149 'register' removed in C++17 { register int toggle=0; @y diff --git a/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex index 09e222d3c3a..dd8b43bea93 100644 --- a/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex +++ b/Build/source/texk/web2c/cwebdir/texinputs/dcwebmac.tex @@ -28,7 +28,7 @@ % Version 3.64 [p21] --- Andreas Scherer, Octobre 2005 % Version 3.64 [p22] --- Andreas Scherer, March 2016 % Version 4.9 --- Andreas Scherer, June 2022 -% Version 4.12 --- Andreas Scherer, May 2024 +% Version 4.12 --- Andreas Scherer, August 2024 \ifx\undefined\botofcontents\input cwebmac.tex\fi diff --git a/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex index 82d67edb1a5..ff0c0b30bd9 100644 --- a/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex +++ b/Build/source/texk/web2c/cwebdir/texinputs/fcwebmac.tex @@ -28,7 +28,7 @@ % Version 3.64 [p21] --- Andreas Scherer, October 2005 % Version 3.64 [p22] --- Andreas Scherer, March 2016 % Version 4.9 --- Andreas Scherer, June 2022 -% Version 4.12 --- Andreas Scherer, May 2024 +% Version 4.12 --- Andreas Scherer, August 2024 % french translations --- Denis Roegel, 18 September 1994 diff --git a/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex index 5f2e7d3a45a..cbb5696984e 100644 --- a/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex +++ b/Build/source/texk/web2c/cwebdir/texinputs/icwebmac.tex @@ -28,7 +28,7 @@ % Version 3.64 [p21] --- Andreas Scherer, October 2005 % Version 3.64 [p22] --- Andreas Scherer, March 2016 % Version 4.9 --- Andreas Scherer, June 2022 -% Version 4.12 --- Andreas Scherer, May 2024 +% Version 4.12 --- Andreas Scherer, August 2024 \ifx\undefined\botofcontents\input cwebmac.tex\fi @@ -73,7 +73,7 @@ \def\inx{\par\vskip6pt plus 1fil % we are beginning the index \def\page{\box255 } \normalbottom \write\cont{} % ensure that the contents file isn't empty - \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother + \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother \closeout\cont % the contents information has been fully gathered \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi \global\setbox\sbox=\page \global\pagesavedtrue \mark{\topmark}} @@ -82,9 +82,7 @@ \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height \hsize=.5\pagewidth \advance\hsize by -10pt % column width for the index (20pt between cols) - \ifhint\else - \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines - \fi + \ifhint\else\parfillskip 0pt plus .6\hsize\fi % avoid almost empty lines \def\lr{L} % this tells whether the left or right column is next \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} \else\normaloutput{\vbox to\pageheight{\box\sbox\vss @@ -93,9 +91,9 @@ \message{Indice Analitico:} \parskip 0pt plus .5pt \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em - \ifacrohint\pdfnote##2.\else##2\fi.} % index entry - \def\[##1]{$\underline{##1}$} % underlined index item - \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \scan##2!.} % index entry + \def\[##1]{$\underline{\scan##1!}$\scan} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \hyphenpenalty 10000 \parindent0pt \readindex} \def\fin{\par\vfill\eject % this is done when we are ending the index diff --git a/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex b/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex index f41ad4d6334..4079d5f704b 100644 --- a/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex +++ b/Build/source/texk/web2c/cwebdir/texinputs/pdfctproofmac.tex @@ -1,5 +1,5 @@ % standard macros for CWEB listings (in addition to plain.tex) -% Version 4.12 --- May 2024 +% Version 4.12 --- August 2024 % modified for proofs in CTWILL % modified for PDF/HINT output with 'tex "\let\pdf+' or '{hi|lua|pdf|xe}tex' % This file is not copyrighted and can be used freely. @@ -14,15 +14,6 @@ \font\ninerm=cmr9 \let\mc=\ninerm % medium caps -\def\CEE/{{\mc C\spacefactor1000}} -\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} -\def\TEX/{\TeX} -\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} -\def\Cee{\CEE/} % for backward compatibility -\def\Cpp{\CPLUSPLUS/} % for backward compatibility -\def\9#1{} - % with this definition of \9 you can say @:sort key}{TeX code@> - % to alphabetize an index entry by the sort key but format with the TeX code \font\eightrm=cmr8 \let\sc=\eightrm % small caps (NOT a caps-and-small-caps font) \let\mainfont=\tenrm @@ -33,6 +24,16 @@ \font\tentex=cmtex10 % TeX extended character set (used in strings) \fontdimen7\tentex=0pt % no double space after sentences +\def\CEE/{{\mc C\spacefactor1000}} +\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} +\def\Cee{\CEE/} % for backward compatibility +\def\Cpp{\CPLUSPLUS/} % for backward compatibility +\def\9#1{} + % with this definition of \9 you can say @:sort key}{TeX code@> + % to alphabetize an index entry by the sort key but format with the TeX code + \def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers \def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way \def\{\leavevmode\hbox{\bf @@ -116,11 +117,9 @@ \def\note#1#2.{\Y\noindent{\hangindent2em% \baselineskip10pt\eightrm#1~\ifacrohint{\pdfnote#2.}\else#2\fi.\par}} -\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD -\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer +\newtoks\toksA \newtoks\toksE \newcount\countA \countA=0 \newcount\countB \countB=0 -\newcount\countC \countC=0 \newcount\countD \countD=0 -\newif\iftokprocessed \newif\ifTnum \newif\ifinstr +\newcount\countNOS \countNOS=0 {\def\\{\global\let\spacechar= }\\ } % Here we decide the output format, depending on the TeX engine in use: @@ -137,7 +136,7 @@ \def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too \def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}} \fi -\ifacrohint \input cwebacromac \fi % load hypertext macros +\input cwebacromac % load hypertext macros \def\pdfURL#1#2{\ifpdf \ifpdflua\pdfannotlink height \theheight depth \thedepth attr {/Border [0 0 0]} user { /Type /Annot /Subtype /Link /A << /S /URI /URI (#2) >>}\Blue #1\Black \pdfendlink % changed in 3.69 @@ -187,7 +186,7 @@ \outer\def\M#1{\MN{#1}\ifon\vfil\penalty-100\vfilneg % beginning of section \vskip\intersecskip\startsection\ignorespaces} \outer\def\N#1#2#3.{% beginning of starred section - \ifpdf{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi + \ifpdf{\makeoutlinetoks#3\outlinedone}\fi \gdepth=#1\gtitle={#3}\MN{#2}% \ifon\ifnum#1<\secpagedepth \vfil\eject % force page break if depth is small \else\vfil\penalty-100\vfilneg\vskip\intersecskip\fi\fi @@ -327,18 +326,16 @@ \newbox\lbox % lefthand column in the index \def\inx{\par\vskip6pt plus 1fil % we are beginning the index \write\cont{} % ensure that the contents file isn't empty - \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother + \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother \closeout\cont % the contents information has been fully gathered \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi - \global\setbox\sbox=\page \global\pagesavedtrue} + \global\setbox\sbox=\page \global\pagesavedtrue \mark{\topmark}} \pagesavedfalse \eject % eject the page-so-far and predecessors \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height \hsize=.5\pagewidth \advance\hsize by -10pt % column width for the index (20pt between cols) - \ifhint\else - \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines - \fi + \ifhint\else\parfillskip 0pt plus .6\hsize\fi % avoid almost empty lines \def\lr{L} % this tells whether the left or right column is next \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} \else\normaloutput{\vbox to\pageheight{\box\sbox\vss @@ -347,9 +344,9 @@ \message{Index:} \parskip 0pt plus .5pt \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em - \ifacrohint\pdfnote##2.\else##2\fi.} % index entry - \def\[##1]{$\underline{##1}$} % underlined index item - \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \scan##2!.} % index entry + \def\[##1]{$\underline{\scan##1!}$\scan} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \hyphenpenalty 10000 \parindent0pt \readindex} \def\fin{\par\vfill\eject % this is done when we are ending the index @@ -370,23 +367,21 @@ \def\I{\par\hangindent 2em}\let\*=* \ifpdf \def\outsecname{Names of the sections} \let\Xpdf\X \ifpdflua \pdfdest name {NOS} fith - \pdfoutline goto name {NOS} count -\the\countD {\outsecname} - \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% - {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% - \pdfoutline goto num \the\toksA \expandafter{\the\toksE}} + \pdfoutline goto name {NOS} count -\the\countNOS {\outsecname} \else \special{pdf: outline -1 << /Title (\outsecname) - /Dest [ @thispage /FitH @ypos ] >>} - \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% - {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% - \special{pdf: outline 0 << /Title (\the\toksE) - /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}} - \fi\fi + /Dest [ @thispage /FitH @ypos ] >>}\fi + \def\X##1:##2\X{\Xpdf##1:##2\X \firstsecno##1.% + {\makeoutlinetoks##2\outlinedone}% + \ifpdflua \pdfoutline goto num \the\toksA \expandafter{\the\toksE} + \else \special{pdf: outline 0 << /Title (\the\toksE) + /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}\fi} + \fi % \ifacro \readsections} \def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax} \def\countsections{\message{Number of named sections:} - {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax} + {\def\I{\global\advance\countNOS by 1}\def\X##1\X{\relax} \def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax} - \readsections\relax}\message{\the\countD}} + \readsections\relax}\message{\the\countNOS}} \def\expnumber#1{\expandafter\ifx\csname#1\endcsname\relax 0% \else \csname#1\endcsname \fi} % Petr Olsak's macros from texinfo.tex \def\advancenumber#1{\countA=\expnumber{#1}\relax \advance\countA by1 @@ -412,17 +407,14 @@ \line{\consetup{#2}#1 \rm\leaders\hbox to .5em{.\hfil}\hfil \ \ifhint - \HINTlink{#3}{\romannumeral#3}% No page numbers in HINT + \HINTlink{#3}% No page numbers in HINT \HINTcontents{#1}{#2}{#3}% - \else\ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi + \else\ifacro\pdflink{#3}\else#3\fi \hbox to3em{\hss#4}\fi}} \def\consetup#1{\ifcase#1 \bf % depth -1 (@**) \or % depth 0 (@*) \or \hskip2em % depth 1 (@*1) - \or \hskip4em % depth 2 (@*2) - \or \hskip6em % depth 3 (@*3) - \or \hskip8em % depth 4 (@*4) - \or \hskip10em % depth 5 (@*5) + \or \hskip4em \or \hskip6em \or \hskip8em \or \hskip10em % depth 2,3,4,5 \else \hskip12em \fi} % depth 6 or more \def\noinx{\let\inx=\end} % no indexes or table of contents \def\nosecs{\let\FIN=\fin \def\fin{\let\parfillskip=\end \FIN}} diff --git a/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex b/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex index 2a0499949e6..d1278cac3bf 100644 --- a/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex +++ b/Build/source/texk/web2c/cwebdir/texinputs/pdfctwimac.tex @@ -1,5 +1,5 @@ % standard macros for CWEB listings (in addition to plain.tex) -% Version 4.12 --- May 2024 +% Version 4.12 --- August 2024 % modified for pages produced by CTWILL % further modified for page size of the MMIXware book % modified for PDF output with 'tex "\let\pdf+' or '{lua|pdf|xe}tex' @@ -26,15 +26,6 @@ \font\ninerm=cmr9 \let\mc=\ninerm % medium caps -\def\CEE/{{\mc C\spacefactor1000}} -\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} -\def\TEX/{\TeX} -\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} -\def\Cee{\CEE/} % for backward compatibility -\def\Cpp{\CPLUSPLUS/} % for backward compatibility -\def\9#1{} - % with this definition of \9 you can say @:sort key}{TeX code@> - % to alphabetize an index entry by the sort key but format with the TeX code \font\eightrm=cmr8 \font\sixrm=cmr6 \font\ninei=cmmi9 @@ -146,6 +137,16 @@ \font\titlefont=cmr7 scaled\magstep4 % title on the contents page \font\ttitlefont=cmtt10 scaled\magstep2 % typewriter type in title +\def\CEE/{{\mc C\spacefactor1000}} +\def\UNIX/{{\mc U\kern-.05emNIX\spacefactor1000}} +\def\TEX/{\TeX} +\def\CPLUSPLUS/{{\mc C\PP\spacefactor1000}} +\def\Cee{\CEE/} % for backward compatibility +\def\Cpp{\CPLUSPLUS/} % for backward compatibility +\def\9#1{} + % with this definition of \9 you can say @:sort key}{TeX code@> + % to alphabetize an index entry by the sort key but format with the TeX code + \def\\#1{\leavevmode\hbox{\it#1\/\kern.05em}} % italic type for identifiers \def\|#1{\leavevmode\hbox{$#1$}} % one-letter identifiers look better this way \def\{\leavevmode\hbox{\bf @@ -228,11 +229,9 @@ \Y\noindent{\hangindent2\em\baselineskip10pt% \eightrm#1~\ifacro{\pdfnote#2.}\else#2\fi.\par}} -\newtoks\toksA \newtoks\toksB \newtoks\toksC \newtoks\toksD -\newtoks\toksE \newtoks\toksF \newtoks\usersanitizer +\newtoks\toksA \newtoks\toksE \newcount\countA \countA=0 \newcount\countB \countB=0 -\newcount\countC \countC=0 \newcount\countD \countD=0 -\newif\iftokprocessed \newif\ifTnum \newif\ifinstr +\newcount\countNOS \countNOS=0 {\def\\{\global\let\spacechar= }\\ } % Here we decide the output format, depending on the TeX engine in use: @@ -253,7 +252,7 @@ \def\Black{\pdfliteral{0 g 0 G}} % use rgb colors for direct PDF output too \def\Blue{\pdfliteral{\pdflinkcolor\space rg \pdflinkcolor\space RG}} \fi -\ifacrohint \input cwebacromac \fi % load hypertext macros +\input cwebacromac % load hypertext macros \def\lapstar{\rlap{*}} \def\stsec{\tenpoint\rightskip=0pt % get out of C mode (cf. \B) @@ -301,7 +300,7 @@ \outer\def\NN{\ifvoid\partialpage\leftfalse \else\ifdim\ht\partialpage<\pageht \lefttrue\else\leftfalse\fi\fi\NNN} \outer\def\NNN#1#2#3.{% beginning of starred section - \ifacro{\toksF={}\makeoutlinetoks#3\outlinedone\outlinedone}\fi + \ifacro{\makeoutlinetoks#3\outlinedone}\fi \gdepth=#1\gtitle={#3}\def\secno{#2}\xdef\secstar{#2} \ifleft \flushout \gdef\rlhead{\let\i=I\uppercase{\ignorespaces#3}} % running left headline @@ -334,7 +333,7 @@ \def\T#1{\leavevmode % octal, hex or decimal constant \hbox{$\def\?{\kern.2em}%$% \let\ \, % C++ digit separator becomes a little white space - \def\$##1{\egroup_{\rm##1}\bgroup}% suffix to constant + \def\$##1{\egroup_{\rm##1}\bgroup}%$% suffix to constant \def\_{\cdot 10^{\aftergroup}}% power of ten (via dirty trick) \let\~=\oct \let\^=\hex \let\\=\bin {#1}$}}%$% \def\U{\note{This code is used in section}} % xref for use of a section @@ -543,22 +542,20 @@ \def\outsecname{Names of the sections} \ifpdflua \makebookmarks \countsections \pdfdest name {NOS} fith - \pdfoutline goto name {NOS} count -\the\countD {\outsecname} - \def\X##1:##2\X{\firstsecno##1.% - {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% - \pdfoutline goto num \the\toksA \expandafter{\the\toksE}} + \pdfoutline goto name {NOS} count -\the\countNOS {\outsecname} \else \special{pdf: outline -1 << /Title (\outsecname) - /Dest [ @thispage /FitH @ypos ] >>} - \def\X##1:##2\X{\firstsecno##1.% - {\toksF={}\makeoutlinetoks##2\outlinedone\outlinedone}% - \special{pdf: outline 0 << /Title (\the\toksE) - /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}} - \fi \readsections \fi} + /Dest [ @thispage /FitH @ypos ] >>}\fi + \def\X##1:##2\X{\firstsecno##1.% + {\makeoutlinetoks##2\outlinedone}% + \ifpdflua \pdfoutline goto num \the\toksA \expandafter{\the\toksE} + \else \special{pdf: outline 0 << /Title (\the\toksE) + /A << /S /GoTo /D (\romannumeral\the\toksA) >> >>}\fi} + \readsections \fi} \def\makebookmarks{\let\ZZ=\writebookmarkline \readcontents\relax} \def\countsections{\message{Number of named sections:} - {\def\I{\global\advance\countD by 1}\def\X##1\X{\relax} + {\def\I{\global\advance\countNOS by 1}\def\X##1\X{\relax} \def\Q##1.{\relax}\def\Qs##1.{\relax}\def\U##1.{\relax}\def\Us##1.{\relax} - \readsections\relax}\message{\the\countD}} + \readsections\relax}\message{\the\countNOS}} \def\writebookmarkline#1#2#3#4#5{{% \let\(=\let \let\)=\let \let\[=\let \let\]=\let \let\/=\let \pdfoutline goto num #3 count -\expnumber{chunk#2.#3} {#5}}} @@ -579,7 +576,7 @@ %\def\contentsline#1#2#3#4#5{\ifnum#2=0 \smallbreak\fi %\line{\consetup{#2}#1 %\rm\leaders\hbox to .5em{.\hfil}\hfil - %\ \ifacro\pdflink{#3}{\romannumeral#3}\else#3\fi\hbox to3em{\hss#4}}} + %\ \ifacro\pdflink{#3}\else#3\fi\hbox to3em{\hss#4}}} %\def\consetup#1{\ifcase#1 \bf % depth -1 (@**) %\or % depth 0 (@*) %\or \hskip2em % depth 1 (@*1) diff --git a/Build/source/texk/web2c/dvicopy.ch b/Build/source/texk/web2c/dvicopy.ch index 5b4ebbc5438..07b3261641b 100644 --- a/Build/source/texk/web2c/dvicopy.ch +++ b/Build/source/texk/web2c/dvicopy.ch @@ -8,45 +8,45 @@ % Mar 20 91 THD Updated to dvicopy 1.2 % (more recent changes in the ChangeLog) -@x [0] l.59 WEAVE: print changes only +@x [0] l.60 WEAVE: print changes only \pageno=\contentspagenumber \advance\pageno by 1 @y \pageno=\contentspagenumber \advance\pageno by 1 \let\maybe=\iffalse @z -@x [1] l.97 Define my_name +@x [1] l.98 Define my_name @d banner=='This is DVIcopy, Version 1.6' {printed when the program starts} @y @d my_name=='dvicopy' @d banner=='This is DVIcopy, Version 1.6' {printed when the program starts} @z -@x [2] l.123 No random reading on stdin, may be not seekable. +@x [2] l.124 No random reading on stdin, may be not seekable. @d random_reading==true {should we skip around in the file?} @y @<Globals in the outer block@>= @!random_reading:boolean; {should we skip around in the file?} @z -@x [3] l.134 Purge non-local 'goto'. +@x [3] l.135 Purge non-local 'goto'. calls the `|jump_out|' procedure, which goes to the label |final_end|. @d final_end = 9999 {go here to wrap it up} @y calls the `|jump_out|' procedure. @z -@x [3] l.140 +@x [3] l.141 label final_end; @y @z -@x [3] l.145 Set up kpathsea. +@x [3] l.146 Set up kpathsea. procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin @@ -55,7 +55,7 @@ procedure initialize; {this procedure gets things started properly} print (banner); print_ln (version_string); @z -@x [5] l.177 Big enough arrays to convert the dvilj sample font document. +@x [5] l.178 Big enough arrays to convert the dvilj sample font document. @<Constants...@>= @!max_fonts=100; {maximum number of distinct fonts} @!max_chars=10000; {maximum number of different characters among all fonts} @@ -85,7 +85,7 @@ procedure initialize; {this procedure gets things started properly} % [7] Rename the integer types, as they collide with names used by C99. % Rather than change the code all over the place, we use macros to do % the renaming. This could also be done at C preprocessor level. -@x l.244 +@x l.245 @d int_32 == integer {signed 32~bit integers} @y @d int_32 == integer {signed 32~bit integers} @@ -103,7 +103,7 @@ procedure initialize; {this procedure gets things started properly} % [11] Redirect output, so it can go to either stdout or stderr, % depending on where the output dvi file is going. -@x l.310 +@x l.311 @d print(#)==write(output,#) @d print_ln(#)==write_ln(output,#) @d new_line==write_ln(output) {start new line} @@ -113,7 +113,7 @@ procedure initialize; {this procedure gets things started properly} @d new_line==write_ln(term_out) {start new line} @z -@x [14] l.379 Permissive input. +@x [14] l.380 Permissive input. @!ASCII_code=" ".."~"; {a subrange of the integers} @y @!ASCII_code=0..255; {a subrange of the integers} @@ -122,7 +122,7 @@ procedure initialize; {this procedure gets things started properly} % [15] 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 l.399 +@x l.400 @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|} @@ -132,7 +132,7 @@ procedure initialize; {this procedure gets things started properly} @d last_text_char=255 {ordinal number of the largest element of |text_char|} @z -@x [23] l.572 Remove non-local goto, declare jump_out as noreturn +@x [23] l.573 Remove non-local goto, declare jump_out as noreturn so a procedure called |jump_out| has been introduced. This procedure, which transfers control to the label |final_end| at the end of the program, contains the only non-local |@!goto| statement in \.{\title}. @@ -143,7 +143,7 @@ by a call on some system procedure that quietly terminates the program. @y so a procedure called |jump_out| has been introduced. @z -@x [23] l.581 +@x [23] l.582 @d abort(#)==begin print_ln(' ',#,'.'); jump_out; end @@ -170,31 +170,31 @@ uexit(1); end; @z -@x [24] l.596 Declare confusion as noreturn +@x [24] l.597 Declare confusion as noreturn says |confusion(|indication of where we are|)|. @y says |confusion|(indication of where we are). @z -@x [24] l.599 +@x [24] l.600 procedure confusion(@!p:pckt_pointer); @y noreturn procedure confusion(@!p:pckt_pointer); @z -@x [25] l.608 Declare overflow as noreturn +@x [25] l.609 Declare overflow as noreturn procedure overflow(@!p:pckt_pointer;@!n:int_16u); @y noreturn procedure overflow(@!p:pckt_pointer;@!n:int_16u); @z -@x [62] l.1235 cur_name is no longer a fixed-size array. +@x [62] l.1236 cur_name is no longer a fixed-size array. @!cur_name:packed array[1..name_length] of char; {external name, with no lower case letters} @y @!cur_name:^char; @z -@x [63] l.1240 Ignore arguments to make_font_name. +@x [63] l.1241 Ignore arguments to make_font_name. @ For \.{TFM} and \.{VF} files we just append the appropriate extension to the file name packet; in addition a system dependent area part (usually different for \.{TFM} and \.{VF} files) is prepended if @@ -229,24 +229,24 @@ to |make_font_name|. % [67] No conversion of filenames in lower case, and initialize and % terminate for C strings. Eliminate now unused variable. -@x l.1310 +@x l.1311 @!c:char; {a character to be appended to |cur_name|} @y @z -@x [67] l.1312 +@x [67] l.1313 cur_loc:=pckt_start[n]; cur_limit:=pckt_start[n+1]; @y cur_name := xmalloc_array (char, pckt_length (n) + pckt_length (e)); cur_loc:=pckt_start[n]; cur_limit:=pckt_start[n+1]; @z -@x [67] l.1317 +@x [67] l.1318 if (b>="a")and(b<="z") then Decr(b)(("a"-"A")); {convert to upper case} @y @z -@x [67] l.1320 +@x [67] l.1321 cur_loc:=pckt_start[e]; cur_limit:=pckt_start[e+1]; while cur_loc<cur_limit do begin pckt_extract(b); append_res_to_name(xchr[b]); @@ -258,13 +258,13 @@ while l_cur_name<name_length do cur_name[l_cur_name] := 0; @z -@x [91] l.1716 Lower case file name extensions. +@x [91] l.1717 Lower case file name extensions. id4(".")("T")("F")("M")(tfm_ext); {file name extension for \.{TFM} files} @y id4(".")("t")("f")("m")(tfm_ext); {file name extension for \.{TFM} files} @z -@x [92] l.1718 Set default directory name +@x [92] l.1719 Set default directory name @ If no font directory has been specified, \.{\title} is supposed to use the default \.{TFM} directory, which is a system-dependent place where the \.{TFM} files for standard fonts are kept. @@ -280,26 +280,26 @@ The string variable |TFM_default_area| contains the name of this area. @ If no font directory has been specified, we search paths. @z -@x [93] l.1730 Remove initialization of now-defunct array +@x [93] l.1731 Remove initialization of now-defunct array @ @<Set init...@>= TFM_default_area:=TFM_default_area_name; @y @ (No initialization to be done. Keep this module to preserve numbering.) @z -@x [94] l.1738 Declare bad_tfm as noreturn +@x [94] l.1739 Declare bad_tfm as noreturn procedure bad_tfm; @y noreturn procedure bad_tfm; @z -@x [94] l.1745 Declare bad_font as noreturn +@x [94] l.1746 Declare bad_font as noreturn procedure bad_font; @y noreturn procedure bad_font; @z -@x [95] l.1757 Open TFM file +@x [95] l.1758 Open TFM file @<TFM: Open |tfm_file|@>= make_font_name(TFM_default_area_name_length)(TFM_default_area)(tfm_ext); reset(tfm_file,cur_name); @@ -318,7 +318,7 @@ if full_name then begin end else @z -@x [104] l.1990 Compute alpha and beta. +@x [104] l.1991 Compute alpha and beta. alpha:=16; while z>=@'40000000 do @y @@ -327,20 +327,20 @@ if z>=@'1000000000 then abort('Character size is too large!'); while z>=@'40000000 do @z -@x [108] l.2060 Declare full_name. +@x [108] l.2061 Declare full_name. @!dvi_loc:int_32; {where we are about to look, in |dvi_file|} @y @!dvi_loc:int_32; {where we are about to look, in |dvi_file|} @!full_name: ^char; @z -@x [109] l.2067 Declare bad_dvi as noreturn +@x [109] l.2068 Declare bad_dvi as noreturn procedure bad_dvi; @y noreturn procedure bad_dvi; @z -@x [110] l.2076 Fix up opening the binary files +@x [110] l.2077 Fix up opening the binary files @ To prepare |dvi_file| for input, we |reset| it. @<Open input file(s)@>= @@ -353,7 +353,7 @@ dvi_loc:=0; dvi_loc:=0; @z -@x [112] l.2114 Make dvi_length() and dvi_move() work. +@x [112] l.2115 Make dvi_length() and dvi_move() work. @p function dvi_length:int_32; begin set_pos(dvi_file,-1); dvi_length:=cur_pos(dvi_file); end; @@ -374,13 +374,13 @@ dvi_loc:=n; end; @z -@x [135] l.2467 Lower case file name extensions. +@x [135] l.2468 Lower case file name extensions. id3(".")("V")("F")(vf_ext); {file name extension for \.{VF} files} @y id3(".")("v")("f")(vf_ext); {file name extension for \.{VF} files} @z -@x [137] l.2481 Set default directory name +@x [137] l.2482 Set default directory name @ If no font directory has been specified, \.{\title} is supposed to use the default \.{VF} directory, which is a system-dependent place where the \.{VF} files for standard fonts are kept. @@ -407,7 +407,7 @@ by setting the \.{VFFONTS} environment variable. @ (No initialization to be done. Keep this module to preserve numbering.) @z -@x [139] l.2498 Open VF file +@x [139] l.2499 Open VF file @<VF: Open |vf_file| or |goto not_found|@>= make_font_name(VF_default_area_name_length)(VF_default_area)(vf_ext); reset(vf_file,cur_name); @@ -426,7 +426,7 @@ if full_name then begin end else @z -@x [163] l.2937 copy elements of array piece by piece +@x [163] l.2938 copy elements of array piece by piece @ @<VF: Start a new level@>= append_one(push); vf_move[vf_ptr]:=vf_move[vf_ptr-1]; @@ -444,19 +444,19 @@ append_one(push); vf_move_assign; @z -@x [170] l.3033 and again... +@x [170] l.3034 and again... vf_move[vf_ptr]:=vf_move[vf_ptr-1]; @y vf_move_assign; @z -@x [176] l.3138 break is fflush. +@x [176] l.3139 break is fflush. @d update_terminal == break(output) {empty the terminal output buffer} @y @d update_terminal == fflush(stdout) {empty the terminal output buffer} @z -@x [176] l.3148 +@x [176] l.3149 procedure input_ln; {inputs a line from the terminal} var k:0..terminal_line_length; begin if n_opt=0 then @@ -484,42 +484,42 @@ while (k<terminal_line_length)and not eoln(input) do end; @z -@x [241] l.4070 No dialog, remove unused final label. +@x [241] l.4071 No dialog, remove unused final label. dialog; {get options} @y @z -@x [241] l.4075 +@x [241] l.4076 final_end:end. @y end. @z -@x [246] l.4124 Do this later, to avoid creating empty files. +@x [246] l.4125 Do this later, to avoid creating empty files. @<Open output file(s)@>= rewrite(out_file); {prepares to write packed bytes to |out_file|} @y @<Open output file(s)@>= @z -@x [248] l.4141 Use external routine to output bytes. +@x [248] l.4142 Use external routine to output bytes. @d out_byte(#) == write(out_file,#) {write next \.{DVI} byte} @y @d out_byte(#) == put_byte(#,out_file) {write next \.{DVI} byte} @z -@x [260] l.4259 String declaration. +@x [260] l.4260 String declaration. @!comment:packed array[1..comm_length] of char; {preamble comment prefix} @y @!comment:const_c_string; {preamble comment prefix} @z -@x [261] l.4270 Output the string from 0 to len-1, not 1 to len. +@x [261] l.4271 Output the string from 0 to len-1, not 1 to len. for k:=1 to comm_length do append_byte(xord[comment[k]]); @y for k:=0 to comm_length - 1 do append_byte(xord[ucharcast(comment[k])]); @z -@x [293] l.4480 System-dependent changes. +@x [293] l.4481 System-dependent changes. This section should be replaced, if necessary, by changes to the program that are necessary to make \.{DVIcopy} work at a particular installation. It is usually best to design your change file so that all changes to @@ -536,7 +536,7 @@ by the index variable |option_index|. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 5; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/dvicopy.web b/Build/source/texk/web2c/dvicopy.web index e24ad85b03a..8220a18ff02 100644 --- a/Build/source/texk/web2c/dvicopy.web +++ b/Build/source/texk/web2c/dvicopy.web @@ -22,6 +22,7 @@ % bug fix (not for Web2C) and some typos (May 2014) % from Udo Wermuth (u.wermuth@@icloud.com). % 17 July 2018, still version 1.6: typos corrected, no code changes. +% 02 Sep 2024, still version 1.6: fix sorting order of names-of-sections. % Here is TeX material that gets inserted after \input webmac \def\hang{\hangindent 3em\indent\ignorespaces} @@ -32,7 +33,7 @@ \def\MF{{\logo META}\-{\logo FONT}} \mathchardef\RA="3221 % right arrow -\def\(#1){} % this is used to make section names sort themselves better +\def\({} % 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{copy}} % don't change this line! @@ -1747,7 +1748,7 @@ begin new_line; case font_type(cur_fnt) of defined_font: confusion(str_fonts); loaded_font: bad_tfm; - @<Cases for |bad_font|@>@;@/ + @<Cases for \(|bad_font|@>@;@/ othercases abort('internal error'); endcases; end; @@ -2470,7 +2471,7 @@ id3(".")("V")("F")(vf_ext); {file name extension for \.{VF} files} gives an error message which refers the user to \.{VFtoVP} and \.{VPtoVF}, and terminates \.{\title}. -@<Cases for |bad_font|@>= +@<Cases for \(|bad_font|@>= vf_font_type: begin print('Bad VF file'); print_font(cur_fnt); @.Bad VF file@> print_ln(' loc=',vf_loc:1); @@ -3523,7 +3524,7 @@ routine determines whether a page shall be processed or skipped and sets the variable |type_setting| accordingly. @p procedure do_bop;@/ -@<OUT: Declare additional local variables |do_bop|@>@; +@<OUT: Declare additional local variables for |do_bop|@>@; @!i,@!j:0..9; {indices into |count|} begin @<Determine whether this page should be processed or skipped@>; print('DVI: '); @@ -3642,7 +3643,7 @@ be updated (\\{set} commands). The |do_rule| procedure is called in order to typeset a rule. @p procedure do_rule;@/ -@<OUT: Declare additional local variables |do_rule|@>@; +@<OUT: Declare additional local variables for |do_rule|@>@; @!visible:boolean; begin h_rule_pixels@; if (cur_h_dimen>0)and(cur_v_dimen>0) then @@ -3663,7 +3664,7 @@ character~|cur_res| with extension~|cur_ext| from the real font~|cur_fnt|. @p procedure do_char;@/ @<OUT: Declare local variables (if any) for |do_char|@>@; -begin @<OUT: Typeset a |char|@>@; +begin @<OUT: Typeset a \(|char|@>@; if cur_upd then begin h_upd_char(widths[cur_wp])(char_pixels[cur_cp]); @<OUT: Move right@>@; @@ -4284,7 +4285,7 @@ for r:=p to q-1 do out_one(bo(byte_mem[r])); @ These are the additional local variables (if any) needed for |do_bop|; the variables |@!i| and |@!j| are already declared. -@<OUT: Declare additional local variables |do_bop|@>= +@<OUT: Declare additional local variables for |do_bop|@>= var @ And here is the device dependent code for |do_bop|. @@ -4378,7 +4379,7 @@ out_four(width_dimen); out_four(cur_h_dimen); @ These are the additional local variables (if any) needed for |do_rule|; the variable |@!visible| is already declared. -@<OUT: Declare additional local variables |do_rule|@>= +@<OUT: Declare additional local variables for |do_rule|@>= var @ And here is the device dependent code for |do_rule|. @@ -4421,7 +4422,7 @@ out_fnt_def(cur_fnt); @ And here is the device dependent code for |do_char|. -@<OUT: Typeset a |char|@>= +@<OUT: Typeset a \(|char|@>= @!debug if font_type(cur_fnt)<>out_font_type then confusion(str_fonts); gubed @; if cur_fnt<>out_fnt then diff --git a/Build/source/texk/web2c/dvitype.ch b/Build/source/texk/web2c/dvitype.ch index c22c080a6de..d17904ff001 100644 --- a/Build/source/texk/web2c/dvitype.ch +++ b/Build/source/texk/web2c/dvitype.ch @@ -20,6 +20,12 @@ % 01/16/90 (SR) To version 3.2. % (more recent changes in the ChangeLog) +@x [0] l.29 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.32 \def\title{DVI$\,$\lowercase{type}} @y @@ -55,7 +61,7 @@ procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} begin @@ -535,7 +541,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 8; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/euptexdir/ChangeLog b/Build/source/texk/web2c/euptexdir/ChangeLog index 2f494fb0d90..5ec9d17d0a9 100644 --- a/Build/source/texk/web2c/euptexdir/ChangeLog +++ b/Build/source/texk/web2c/euptexdir/ChangeLog @@ -1,3 +1,29 @@ +2024-09-30 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * eptex.ech: kcatcode (latin_ucs(14)) is removed + since it will be supported in upTeX-2.00. + +2024-09-28 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * euptex-{ctrlsym,kinsoku,prntknj}.test: + Skip tests with updvitype if updvitype are not built. + +2024-09-21 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * eptex.ech, pdfutils.ch: + Better support for kcatcode=20 (modifier). + * euptex-{ctrlsym,kinsoku,prntknj,ucs}.test, + tests/{control-symbol,ctrlsym.tex,endlinechar,free_{ixsp,pena}, + inhibitglue,kinsoku_{search,table},postpena,printkanji,ucs}.tex, + tests/eup-{endline,inhibitglue,jctrlsym,pknj-{e,s,u}}a.typ, + tests/{endline,free_{ixsp,pena},jctrlsym,kinsoku_{search,table}, + pknj-{e,s,u},postpena,ucs-{e,s,u}}.log: + Add regression tests by using Kitagawa-san's and + Yamashita-san's sample files. + * tests/{free_{ixsp,pena},inhibitglue,kinsoku_{search,table}, + postpena}.tex: Move from ../ptexdir/tests . + * am/euptex.am: Adjust. + 2024-06-04 Akira Kakuto <kakuto@jcom.zaq.ne.jp> * unbalanced-braces-eptex.ch, suppresserrors.ch: sync with the upstream diff --git a/Build/source/texk/web2c/euptexdir/am/euptex.am b/Build/source/texk/web2c/euptexdir/am/euptex.am index f961b447fcc..0ee6b050f71 100644 --- a/Build/source/texk/web2c/euptexdir/am/euptex.am +++ b/Build/source/texk/web2c/euptexdir/am/euptex.am @@ -98,16 +98,21 @@ EXTRA_DIST += \ euptex_tests = \ euptexdir/euptriptest.test euptexdir/eptriptest.test \ euptexdir/pdfprimitive.test \ - euptexdir/euptex-ctrlsym.test \ euptexdir/eupver.test \ euptexdir/upkcat.test \ - euptexdir/wcfname.test euptexdir/wcfname0.test + euptexdir/wcfname.test euptexdir/wcfname0.test \ + euptexdir/euptex-ctrlsym.test \ + euptexdir/euptex-prntknj.test \ + euptexdir/euptex-kinsoku.test \ + euptexdir/euptex-ucs.test euptexdir/euptriptest.log euptexdir/eptriptest.log: \ euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) euptexdir/pdfprimitive.log: euptex$(EXEEXT) -euptexdir/euptex-ctrlsym.log \ - euptexdir/eupver.log euptexdir/upkcat.log \ - euptexdir/wcfname.log euptexdir/wcfname0.log: euptex$(EXEEXT) +euptexdir/eupver.log euptexdir/upkcat.log \ + euptexdir/wcfname.log euptexdir/wcfname0.log \ + euptexdir/euptex-ctrlsym.log euptexdir/euptex-prntknj.log \ + euptexdir/euptex-kinsoku.log \ + euptexdir/euptex-ucs.log: euptex$(EXEEXT) EXTRA_DIST += $(euptex_tests) @@ -125,11 +130,6 @@ EXTRA_DIST += \ euptexdir/eptrip/eptrip.tex \ euptexdir/euptrip/euptrip.diffs \ euptexdir/euptrip/texmf.cnf -euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) - $(triptrap_diffs) $@ -.PHONY: euptrip-clean -euptrip-clean: - rm -rf euptripdir DISTCLEANFILES += euptrip.diffs ## euptexdir/pdfprimitive.test EXTRA_DIST += \ @@ -139,7 +139,35 @@ EXTRA_DIST += \ DISTCLEANFILES += pdfprimitive-euptex.* ## euptex-ctrlsym.test -EXTRA_DIST += euptexdir/tests/ctrlsym.log euptexdir/tests/ctrlsym.tex -DISTCLEANFILES += eup-ctrlsym.fmt eup-ctrlsym.log eup-ctrlsym.out eup-ctrlsym.tex +EXTRA_DIST += euptexdir/tests/ctrlsym.tex euptexdir/tests/ctrlsym.log \ + euptexdir/tests/endlinechar.tex euptexdir/tests/endline.log \ + euptexdir/tests/control-symbol.tex euptexdir/tests/jctrlsym.log \ + euptexdir/tests/eup-endlinea.typ euptexdir/tests/eup-jctrlsyma.typ +DISTCLEANFILES += eup-ctrlsym.log eup-ctrlsym.out eup-ctrlsym.tex \ + eup-endline*.* eup-jctrlsym*.* +## euptex-prntknj.test +EXTRA_DIST += euptexdir/tests/printkanji.tex euptexdir/tests/pknj-s.log \ + euptexdir/tests/pknj-e.log euptexdir/tests/pknj-u.log \ + euptexdir/tests/eup-pknj-sa.typ euptexdir/tests/eup-pknj-ea.typ \ + euptexdir/tests/eup-pknj-ua.typ +DISTCLEANFILES += eup-pknj-*.* +## euptex-kinsoku.test +EXTRA_DIST += euptexdir/tests/postpena.tex euptexdir/tests/postpena.log \ + euptexdir/tests/free_ixsp.tex euptexdir/tests/free_ixsp.log \ + euptexdir/tests/free_pena.tex euptexdir/tests/free_pena.log \ + euptexdir/tests/inhibitglue.tex euptexdir/tests/eup-inhibitgluea.typ \ + euptexdir/tests/kinsoku-table.tex euptexdir/tests/kinsoku-table.log \ + euptexdir/tests/kinsoku-search.tex euptexdir/tests/kinsoku-search.log +DISTCLEANFILES += eup-postpena*.* eup-free_*.* eup-inhibitglue*.* eup-kinsoku*.* +## euptex-ucs.test +EXTRA_DIST += euptexdir/tests/ucs.tex euptexdir/tests/ucs-s.log \ + euptexdir/tests/ucs-e.log euptexdir/tests/ucs-u.log +DISTCLEANFILES += eup-ucs*.* + +euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) + $(triptrap_diffs) $@ +.PHONY: euptrip-clean +euptrip-clean: + rm -rf euptripdir # (end of euptex.am) diff --git a/Build/source/texk/web2c/euptexdir/eptex.ech b/Build/source/texk/web2c/euptexdir/eptex.ech index a33ecad2bf3..348d3a3e306 100644 --- a/Build/source/texk/web2c/euptexdir/eptex.ech +++ b/Build/source/texk/web2c/euptexdir/eptex.ech @@ -309,6 +309,14 @@ end @d if_tdir_code=if_case_code+4 { `\.{\\iftdir}' } @z +@x procedure conditional +@!m,@!n:integer; {to be tested against the second operand} +@!p,@!q:pointer; {for traversing token lists in \.{\\ifx} tests} +@y +@!m,@!n,@!s,@!v,@!nn,@!jj:integer; {to be tested against the second operand} +@!p,@!q:pointer; {for traversing token lists in \.{\\ifx} tests} +@z + @x [30.???] |scan_font_ident|: recognize \lastnodefont else begin print_err("Missing font identifier"); @y @@ -721,7 +729,6 @@ if j=1 then if (multistrlen(ustringcast(buffer), limit+1,loc)>1)and check_kcat_code(cur_tok,cur_chr) then begin if (cur_tok=not_cjk) then cur_tok:=other_kchar; - if (cur_tok=latin_ucs) then cur_tok:=other_token; cur_tok:=cur_chr+cur_tok*max_cjk_val; loc:=loc+multistrlen(ustringcast(buffer), limit+1,loc); end @@ -736,13 +743,35 @@ if j=1 then @y if check_kanji(info(p)) then {|wchar_token|} begin - if BYTE1(toBUFF(info(p) mod max_cjk_val))<>0 then - begin buffer[m]:=BYTE1(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); end; - if BYTE2(toBUFF(info(p) mod max_cjk_val))<>0 then - begin buffer[m]:=BYTE2(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); end; - if BYTE3(toBUFF(info(p) mod max_cjk_val))<>0 then - begin buffer[m]:=BYTE3(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); end; - buffer[m]:=BYTE4(toBUFF(info(p) mod max_cjk_val)); buffer2[m]:=1; incr(m); + if (isinternalUPTEX) then begin + s:=toUCS(ktokentochr(info(p))); + nn:=UVSgetcodepointlength(s); + jj:=1; + while jj<=nn do begin + v:=UVSgetcodepointinsequence(s,jj); + if (v>0) then begin + v:=UCStoUTF8(v); + if BYTE1(v)<>0 then + begin buffer[m]:=BYTE1(v); buffer2[m]:=1; incr(m); end; + if BYTE2(v)<>0 then + begin buffer[m]:=BYTE2(v); buffer2[m]:=1; incr(m); end; + if BYTE3(v)<>0 then + begin buffer[m]:=BYTE3(v); buffer2[m]:=1; incr(m); end; + buffer[m]:=BYTE4(v); buffer2[m]:=1; incr(m); + end; + incr(jj); + end + end + else begin + v:=toBUFF(info(p) mod max_cjk_val); + if BYTE1(v)<>0 then + begin buffer[m]:=BYTE1(v); buffer2[m]:=1; incr(m); end; + if BYTE2(v)<>0 then + begin buffer[m]:=BYTE2(v); buffer2[m]:=1; incr(m); end; + if BYTE3(v)<>0 then + begin buffer[m]:=BYTE3(v); buffer2[m]:=1; incr(m); end; + buffer[m]:=BYTE4(v); buffer2[m]:=1; incr(m); + end; p:=link(p); end else diff --git a/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test b/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test index 294e0cf0bf1..7284d8c6c35 100755 --- a/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test +++ b/Build/source/texk/web2c/euptexdir/euptex-ctrlsym.test @@ -5,24 +5,59 @@ BinDir=${BinDir:-.} ExeExt=${ExeExt:-} _euptex=$BinDir/euptex$ExeExt +_updvitype=$BinDir/updvitype$ExeExt LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFONTS=".;$srcdir//tests"; export TEXFONTS TEXFORMATS=.; export TEXFORMATS -# get same filename in log -rm -f eup-ctrlsym.tex -$LN_S $srcdir/euptexdir/tests/ctrlsym.tex ./eup-ctrlsym.tex - -#exit 77 +rm -f eup-ctrlsym.* eup-endline*.* eup-jctrlsym*.* -$_euptex -ini -etex eup-ctrlsym || exit 1 +if test ! -f ./eup-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./eup-basic.tex +fi +if test ! -f ./eup-basic.fmt; then + $_euptex -ini -etex eup-basic || exit 1 +fi -$_euptex -etex -fmt=eup-ctrlsym eup-ctrlsym || exit 2 +# ctrlsym.tex +$LN_S $srcdir/euptexdir/tests/ctrlsym.tex ./eup-ctrlsym.tex +$_euptex -etex -fmt=eup-basic eup-ctrlsym || exit 2 sed 1d eup-ctrlsym.log >eup-ctrlsym.out diff $srcdir/euptexdir/tests/ctrlsym.log eup-ctrlsym.out || exit 3 + +# endlinchar.tex +$LN_S $srcdir/euptexdir/tests/endlinechar.tex ./eup-endline.tex +$_euptex -fmt=eup-basic -interaction=batchmode eup-endline.tex || : + +sed 1d eup-endline.log >eup-endline.out + +diff $srcdir/euptexdir/tests/endline.log eup-endline.out || exit 4 + +if test -f $_updvitype; then + $_updvitype ./eup-endline.dvi >euptests/xeup-endline.typ + sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-endline.typ >euptests/xeup-endlinea.typ + diff $srcdir/euptexdir/tests/eup-endlinea.typ euptests/xeup-endlinea.typ || exit 5 +fi + + +# control-symbol.tex +$LN_S $srcdir/euptexdir/tests/control-symbol.tex ./eup-jctrlsym.tex +$_euptex -fmt=eup-basic -interaction=batchmode eup-jctrlsym.tex || : + +sed 1d eup-jctrlsym.log >eup-jctrlsym.out + +diff $srcdir/euptexdir/tests/jctrlsym.log eup-jctrlsym.out || exit 6 + +if test -f $_updvitype; then + $_updvitype ./eup-jctrlsym.dvi >euptests/xeup-jctrlsym.typ + sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-jctrlsym.typ >euptests/xeup-jctrlsyma.typ + diff $srcdir/euptexdir/tests/eup-jctrlsyma.typ euptests/xeup-jctrlsyma.typ || exit 7 +fi + diff --git a/Build/source/texk/web2c/euptexdir/euptex-kinsoku.test b/Build/source/texk/web2c/euptexdir/euptex-kinsoku.test new file mode 100755 index 00000000000..307b1dd6d5a --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/euptex-kinsoku.test @@ -0,0 +1,69 @@ +#! /bin/sh -vx +# Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +# You may freely use, modify and/or distribute this file. + +BinDir=${BinDir:-.} +ExeExt=${ExeExt:-} +_euptex=$BinDir/euptex$ExeExt +_updvitype=$BinDir/updvitype$ExeExt + +LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE + +TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF +TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFONTS=".;$srcdir//tests"; export TEXFONTS +TEXFORMATS=.; export TEXFORMATS + +rm -f eup-postpena*.* eup-free_*.* eup-inhibitglue*.* eup-kinsoku*.* + +if test ! -f ./eup-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./eup-basic.tex +fi +if test ! -f ./eup-basic.fmt; then + $_euptex -ini -etex eup-basic || exit 1 +fi + +# postpena.tex +$LN_S $srcdir/euptexdir/tests/postpena.tex ./eup-postpena.tex +$_euptex -fmt=eup-basic -interaction=batchmode eup-postpena.tex || : + +sed 1d eup-postpena.log >eup-postpena.out + +diff $srcdir/euptexdir/tests/postpena.log eup-postpena.out || exit 3 + + +# free_ixsp.tex, free_pena.tex +$LN_S $srcdir/euptexdir/tests/free_ixsp.tex ./eup-free_ixsp.tex +$LN_S $srcdir/euptexdir/tests/free_pena.tex ./eup-free_pena.tex +$_euptex -fmt=eup-basic -interaction=batchmode eup-free_ixsp.tex || : +$_euptex -fmt=eup-basic -interaction=batchmode eup-free_pena.tex || : + +sed 1d eup-free_ixsp.log >eup-free_ixsp.out +sed 1d eup-free_pena.log >eup-free_pena.out + +diff $srcdir/euptexdir/tests/free_ixsp.log eup-free_ixsp.out || exit 4 +diff $srcdir/euptexdir/tests/free_pena.log eup-free_pena.out || exit 5 + + +# inhibitglue.tex +$LN_S $srcdir/euptexdir/tests/inhibitglue.tex ./eup-inhibitglue.tex +$_euptex -fmt=eup-basic -interaction=batchmode eup-inhibitglue.tex || : + +if test -f $_updvitype; then + $_updvitype ./eup-inhibitglue.dvi >euptests/xeup-inhibitglue.typ + sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-inhibitglue.typ >euptests/xeup-inhibitgluea.typ + diff $srcdir/euptexdir/tests/eup-inhibitgluea.typ euptests/xeup-inhibitgluea.typ || exit 7 +fi + +# kinsoku_table.tex, kinsoku_search.tex +$LN_S $srcdir/euptexdir/tests/kinsoku_table.tex ./eup-kinsoku_table.tex +$LN_S $srcdir/euptexdir/tests/kinsoku_search.tex ./eup-kinsoku_search.tex +$_euptex -fmt=eup-basic -interaction=batchmode eup-kinsoku_table.tex || : +$_euptex -fmt=eup-basic -interaction=batchmode eup-kinsoku_search.tex || : + +sed 1d eup-kinsoku_table.log >eup-kinsoku_table.out +sed 1d eup-kinsoku_search.log >eup-kinsoku_search.out + +diff $srcdir/euptexdir/tests/kinsoku_table.log eup-kinsoku_table.out || exit 8 +diff $srcdir/euptexdir/tests/kinsoku_search.log eup-kinsoku_search.out || exit 9 + diff --git a/Build/source/texk/web2c/euptexdir/euptex-prntknj.test b/Build/source/texk/web2c/euptexdir/euptex-prntknj.test new file mode 100755 index 00000000000..a8dfa84c271 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/euptex-prntknj.test @@ -0,0 +1,61 @@ +#! /bin/sh -vx +# Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +# You may freely use, modify and/or distribute this file. + +BinDir=${BinDir:-.} +ExeExt=${ExeExt:-} +_euptex=$BinDir/euptex$ExeExt +_updvitype=$BinDir/updvitype$ExeExt + +LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE + +TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF +OFMFONTS=".;$srcdir/tests"; export OFMFONTS +TFMFONTS=".;$srcdir//tests"; export TFMFONTS +TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFORMATS=.; export TEXFORMATS + +rm -f eup-pknj-*.* +$LN_S $srcdir/euptexdir/tests/printkanji.tex ./printkanji.tex + +if test ! -f ./eup-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./eup-basic.tex +fi +if test ! -f ./eup-basic-sjis.fmt; then + $_euptex -ini -etex -kanji-internal=sjis -jobname=eup-basic-sjis eup-basic || exit 1 +fi +if test ! -f ./eup-basic-euc.fmt; then + $_euptex -ini -etex -kanji-internal=euc -jobname=eup-basic-euc eup-basic || exit 2 +fi +if test ! -f ./eup-basic.fmt; then + $_euptex -ini -etex -kanji-internal=uptex -jobname=eup-basic eup-basic || exit 3 +fi + +$_euptex -fmt=eup-basic-sjis -kanji-internal=sjis -jobname=eup-pknj-s -interaction=batchmode printkanji || : +$_euptex -fmt=eup-basic-euc -kanji-internal=euc -jobname=eup-pknj-e -interaction=batchmode printkanji || : +$_euptex -fmt=eup-basic -jobname=eup-pknj-u -interaction=batchmode printkanji || : + +sed "1d;s/strings out of 497[67]..$/strings out of 497XXX/;s/characters out of 6215[789]..$/characters out of 6215XXX/" eup-pknj-s.log >eup-pknj-s.out +sed "1d;s/strings out of 497[67]..$/strings out of 497XXX/;s/characters out of 6215[789]..$/characters out of 6215XXX/" eup-pknj-e.log >eup-pknj-e.out +sed "1d;s/strings out of 497[67]..$/strings out of 497XXX/;s/characters out of 6215[789]..$/characters out of 6215XXX/" eup-pknj-u.log >eup-pknj-u.out + +diff $srcdir/euptexdir/tests/pknj-s.log eup-pknj-s.out || exit 10 +diff $srcdir/euptexdir/tests/pknj-e.log eup-pknj-e.out || exit 11 +diff $srcdir/euptexdir/tests/pknj-u.log eup-pknj-u.out || exit 12 + +if test ! -f $_updvitype; then + exit 0 +fi + +$_updvitype ./eup-pknj-s.dvi >euptests/xeup-pknj-s.typ +$_updvitype ./eup-pknj-e.dvi >euptests/xeup-pknj-e.typ +$_updvitype ./eup-pknj-u.dvi >euptests/xeup-pknj-u.typ + +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-pknj-s.typ >euptests/xeup-pknj-sa.typ +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-pknj-e.typ >euptests/xeup-pknj-ea.typ +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" euptests/xeup-pknj-u.typ >euptests/xeup-pknj-ua.typ + +diff $srcdir/euptexdir/tests/eup-pknj-sa.typ euptests/xeup-pknj-sa.typ || exit 20 +diff $srcdir/euptexdir/tests/eup-pknj-ea.typ euptests/xeup-pknj-ea.typ || exit 21 +diff $srcdir/euptexdir/tests/eup-pknj-ua.typ euptests/xeup-pknj-ua.typ || exit 22 + diff --git a/Build/source/texk/web2c/euptexdir/euptex-ucs.test b/Build/source/texk/web2c/euptexdir/euptex-ucs.test new file mode 100755 index 00000000000..764f6e93a9d --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/euptex-ucs.test @@ -0,0 +1,43 @@ +#! /bin/sh -vx +# Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +# You may freely use, modify and/or distribute this file. + +BinDir=${BinDir:-.} +ExeExt=${ExeExt:-} +_euptex=$BinDir/euptex$ExeExt + +LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE + +TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF +OFMFONTS=".;$srcdir/tests"; export OFMFONTS +TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFORMATS=.; export TEXFORMATS + +rm -f eup-ucs*.* +$LN_S $srcdir/euptexdir/tests/ucs.tex ./eup-ucs.tex + +if test ! -f ./eup-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./eup-basic.tex +fi +if test ! -f ./eup-basic-sjis.fmt; then + $_euptex -ini -etex -kanji-internal=sjis -jobname=eup-basic-sjis eup-basic || exit 1 +fi +if test ! -f ./eup-basic-euc.fmt; then + $_euptex -ini -etex -kanji-internal=euc -jobname=eup-basic-euc eup-basic || exit 2 +fi +if test ! -f ./eup-basic.fmt; then + $_euptex -ini -etex -kanji-internal=uptex -jobname=eup-basic eup-basic || exit 3 +fi + +$_euptex -fmt=eup-basic-sjis -kanji-internal=sjis -jobname=eup-ucs-s -interaction=batchmode eup-ucs || exit 4 +$_euptex -fmt=eup-basic-euc -kanji-internal=euc -jobname=eup-ucs-e -interaction=batchmode eup-ucs || exit 5 +$_euptex -fmt=eup-basic -jobname=eup-ucs-u -interaction=batchmode eup-ucs || exit 6 + +sed 1d eup-ucs-s.log >eup-ucs-s.out +sed 1d eup-ucs-e.log >eup-ucs-e.out +sed 1d eup-ucs-u.log >eup-ucs-u.out + +diff $srcdir/euptexdir/tests/ucs-s.log eup-ucs-s.out || exit 10 +diff $srcdir/euptexdir/tests/ucs-e.log eup-ucs-e.out || exit 11 +diff $srcdir/euptexdir/tests/ucs-u.log eup-ucs-u.out || exit 12 + diff --git a/Build/source/texk/web2c/euptexdir/pdfutils.ch b/Build/source/texk/web2c/euptexdir/pdfutils.ch index f62a48be121..8d22773233b 100644 --- a/Build/source/texk/web2c/euptexdir/pdfutils.ch +++ b/Build/source/texk/web2c/euptexdir/pdfutils.ch @@ -1176,7 +1176,7 @@ Ucharcat_convert_code: and (illegal_Ucharcat_wchar_catcode(cur_val)) then begin print_err("Invalid code ("); print_int(cur_val); @.Invalid code@> - print("), should be in the ranges 1..4, 6..8, 10..13, 16..19"); + print("), should be in the ranges 1..4, 6..8, 10..13, 16..20"); help1("I'm going to use 12 instead of that illegal code value.");@/ error; cat:=12; end else cat:=cur_val; @@ -1184,7 +1184,7 @@ Ucharcat_convert_code: begin if illegal_Ucharcat_wchar_catcode(cur_val) then begin print_err("Invalid code ("); print_int(cur_val); @.Invalid code@> - print("), should be in the ranges 16..19"); + print("), should be in the ranges 16..20"); help1("I'm going to use 18 instead of that illegal code value.");@/ error; cat:=other_kchar; end else cat:=cur_val; diff --git a/Build/source/texk/web2c/euptexdir/tests/control-symbol.tex b/Build/source/texk/web2c/euptexdir/tests/control-symbol.tex new file mode 100644 index 00000000000..be05906602d --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/control-symbol.tex @@ -0,0 +1,108 @@ +\scrollmode +% plain TeX ソース +\font\cmr=cmr10\cmr +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi + +\def\+{A} +\def\+{A} +\def\X{\+} +\def\Y{\+} + +\message{■control symbol} +% 記号類扱いの単文字命令 → control symbol +% (この時の和文文字の挙動が tex-jp-build#37 の問題) +\ifx\kanjiskip\undefined + \catcode`\+=12 + \catcode`\+=12 +\else + \catcode`\+=12 + \kcatcode`+=18 +\fi +\show\X\relax\message{(\meaning\X)} +\show\Y\relax\message{(\meaning\Y)} + +\message{■control word} +% 普通の文字扱いの単文字命令 → control word +\ifx\kanjiskip\undefined + \catcode`\+=11 + \catcode`\+=11 +\else + \catcode`\+=11 + \kcatcode`+=17 +\fi +\show\X\relax\message{(\meaning\X)} +\show\Y\relax\message{(\meaning\Y)} + +\message{■control word} +% 複数文字命令 → control word +\def\ABC{A} +\def\あいう{A} +\def\X{\ABC} +\def\Y{\あいう} +\show\X\relax\message{(\meaning\X)} +\show\Y\relax\message{(\meaning\Y)} + +% csname +\ifx\protected\undefined\else +\catcode`\:=12 +\ifx\kanjiskip\undefined\else +\kcatcode`!=18 +\fi +\message{■csname} +\protected\expandafter\def\csname :AB\endcsname{A} +\protected\expandafter\def\csname !あい\endcsname{A} +\edef\X{\csname :AB\endcsname} +\edef\Y{\csname !あい\endcsname} +\show\X\relax\message{(\meaning\X)} +\show\Y\relax\message{(\meaning\Y)} + +\protected\expandafter\def\csname AB:\endcsname{A} +\protected\expandafter\def\csname あい!\endcsname{A} +\edef\X{\csname AB:\endcsname} +\edef\Y{\csname あい!\endcsname} +\show\X\relax\message{(\meaning\X)} +\show\Y\relax\message{(\meaning\Y)} + +\ifx\enablecjktoken\undefined\else +\message{■not_cjk} +\kcatcode`ア=15 +\kcatcode`!=15 +\catcode"8A=12\relax +\protected\expandafter\def\csname ア\endcsname{A} +\protected\expandafter\def\csname !\endcsname{A} +\edef\X{\csname ア\endcsname} +\edef\Y{\csname !\endcsname} +\show\X\relax\message{(\meaning\X)} +\show\Y\relax\message{(\meaning\Y)} +\fi +\fi + +\message{■行末の和文コントロールシンボル} + + +\kcatcode`】=18 + +\def\】{◆} +\def\@{◆} + +1. \】◇,\@◇ + +2. \】% +◇, \@% +◇ + +3. \】 +◇, \@ +◇ + +4. \】 ◇, \@ ◇ + + +\end + diff --git a/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex b/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex index f007708a2bc..d84a541707b 100644 --- a/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex +++ b/Build/source/texk/web2c/euptexdir/tests/ctrlsym.tex @@ -19,7 +19,8 @@ \count255=`\雪 \relax \message{B: \the\count255. 雪\string\雪 \雪^^J} \message{\expandafter\meaning\csname 雪\endcsname.^^J} - \message{\expandafter\meaning\csname \Uchar"96EA\endcsname.^^J}% + \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"96EA\endcsname.^^J}\fi% + \ifnum\jis"2121="3000 \count4095=0 \count4095=`🍌 \relax \message{^^JC: \the\count4095^^J} @@ -27,7 +28,8 @@ \count4095=`\🍌 \relax \message{D: \the\count4095. 🍌\string\🍌 \🍌^^J} \message{\expandafter\meaning\csname 🍌\endcsname.^^J} - \message{\expandafter\meaning\csname \Uchar"1F34C\endcsname.^^J}% + \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"1F34C\endcsname.^^J}\fi% + \fi %-- \setbox0=\vbox{} % no dvi/xdv/pdf output \endgroup diff --git a/Build/source/texk/web2c/euptexdir/tests/endline.log b/Build/source/texk/web2c/euptexdir/tests/endline.log new file mode 100644 index 00000000000..3c0524db173 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/endline.log @@ -0,0 +1,43 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-endline.tex +(./eup-endline.tex +! Undefined control sequence. +l.29 7a\^^e3 + ^^81^^82b\^^e3^^81^^82 +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.29 7a\^^e3^^81^^82b\^^e3 + ^^81^^82 +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.38 8a\^^e3^^81^^82 + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.42 9a\^^e3^^81^^82 + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +[1] ) +Output written on eup-endline.dvi (1 page, 256 bytes). diff --git a/Build/source/texk/web2c/euptexdir/tests/endlinechar.tex b/Build/source/texk/web2c/euptexdir/tests/endlinechar.tex new file mode 100644 index 00000000000..5dc8de812e7 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/endlinechar.tex @@ -0,0 +1,57 @@ +\font\x=ec-lmr10 \x +\parindent0pt\noautoxspacing +\output{\shipout\box255} +\def\あ{PPP} +\def\,{QQQ} + +1a\, +% --> 1aQQQ + +2a\あ +% --> 2aPPP + +\endlinechar=-1\relax + +3a\, +% --> 3aQQQ +% previous pTeX, upTeX: Undefined cs. + +4a\あ +% --> 4aPPP +% prev. pTeX: Undefined cs. + +5a, +% prev. upTeX: byte-seq. + +6aあ +% prev. upTeX: byte-seq. + +7a\^^e3^^81^^82b\^^e3^^81^^82 +% 2回エラーが出るが,これは正しい. + +% upTeX only tests +\ifx\enablecjktoken\undefined +\else + \catcode"E3=11\relax + \catcode"81=11\relax + \catcode"82=11\relax + 8a\^^e3^^81^^82 + % (p3.10.0-u1.27) --> 8aPPP + % (p4.0.0-u1.27) --> エラー 8a + \kcatcode`あ=15\relax + 9a\あ + % == 9a\^^e3^^81^^82. + % (p3.10.0-u1.27) --> 9aPPP + % (p4.0.0-u1.27) --> エラー 9a + \def\^^e3^^81^^82{RRR} + 10a\あ b\あ + % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 10aRRRbRRR + \kcatcode12354=16\relax + 11a\あ b\あ + % == 11a\^^e3^^81^^82 b\^^e3^^81^^82. + % (p3.10.0-u1.27) --> 11aRRRbRRR + % (p4.0.0-u1.27) --> 11aPPPbPPP +\fi + +\bye + diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ b/Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ new file mode 100644 index 00000000000..1cbfa6b7501 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/eup-endlinea.typ @@ -0,0 +1,93 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 191. +maxv=42152922, maxh=30785863, maxstackdepth=1, totalpages=1 +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 1 +87: down3 655360 v:=0+655360=655360, vv:=42 +91: push +level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +92: fntdef1 0: ec-lmr10 +116: fntnum0 current font is ec-lmr10 +117: setchar49 ("31) h:=0+327680=327680, hh:=21 +118: setchar97 ("61) h:=327680+327680=655360, hh:=42 +119: setchar81 ("51) h:=655360+509738=1165098, hh:=74 +120: setchar81 ("51) h:=1165098+509738=1674836, hh:=106 +121: setchar81 ("51) h:=1674836+509738=2184574, hh:=138 +[1aQQQ] +122: pop +level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +123: y3 786432 v:=655360+786432=1441792, vv:=91 +127: push +level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +128: setchar50 ("32) h:=0+327680=327680, hh:=21 +129: setchar97 ("61) h:=327680+327680=655360, hh:=42 +130: setchar80 ("50) h:=655360+446005=1101365, hh:=70 +131: setchar80 ("50) h:=1101365+446005=1547370, hh:=98 +132: setchar80 ("50) h:=1547370+446005=1993375, hh:=126 +[2aPPP] +133: pop +level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +134: y0 786432 v:=1441792+786432=2228224, vv:=141 +135: push +level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +136: setchar51 ("33) h:=0+327680=327680, hh:=21 +137: setchar97 ("61) h:=327680+327680=655360, hh:=42 +138: setchar81 ("51) h:=655360+509738=1165098, hh:=74 +139: setchar81 ("51) h:=1165098+509738=1674836, hh:=106 +140: setchar81 ("51) h:=1674836+509738=2184574, hh:=138 +141: setchar52 ("34) h:=2184574+327680=2512254, hh:=159 +142: setchar97 ("61) h:=2512254+327680=2839934, hh:=180 +143: setchar80 ("50) h:=2839934+446005=3285939, hh:=208 +144: setchar80 ("50) h:=3285939+446005=3731944, hh:=236 +145: setchar80 ("50) h:=3731944+446005=4177949, hh:=264 +146: setchar53 ("35) h:=4177949+327680=4505629, hh:=285 +147: setchar97 ("61) h:=4505629+327680=4833309, hh:=306 +148: setchar54 ("36) h:=4833309+327680=5160989, hh:=327 +149: setchar97 ("61) h:=5160989+327680=5488669, hh:=348 +150: setchar55 ("37) h:=5488669+327680=5816349, hh:=369 +151: setchar97 ("61) h:=5816349+327680=6144029, hh:=390 +[3aQQQ4aPPP5a6a7a] +152: set1 129("81) h:=6144029+491520=6635549, hh:=421 +154: w2 -18205 h:=6635549-18205=6617344, hh:=420 +157: set1 130("82) h:=6617344+473301=7090645, hh:=450 +159: setchar98 ("62) h:=7090645+364085=7454730, hh:=473 +[b] +160: set1 129("81) h:=7454730+491520=7946250, hh:=504 +162: w0 -18205 h:=7946250-18205=7928045, hh:=503 +163: set1 130("82) h:=7928045+473301=8401346, hh:=533 +165: setchar56 ("38) h:=8401346+327680=8729026, hh:=554 +166: setchar97 ("61) h:=8729026+327680=9056706, hh:=575 +167: setchar57 ("39) h:=9056706+327680=9384386, hh:=596 +168: setchar97 ("61) h:=9384386+327680=9712066, hh:=617 +169: setchar49 ("31) h:=9712066+327680=10039746, hh:=638 +170: setchar48 ("30) h:=10039746+327680=10367426, hh:=659 +171: setchar97 ("61) h:=10367426+327680=10695106, hh:=679 +172: setchar82 ("52) h:=10695106+482410=11177516, hh:=710 +173: setchar82 ("52) h:=11177516+482410=11659926, hh:=741 +174: setchar82 ("52) h:=11659926+482410=12142336, hh:=771 +175: setchar98 ("62) h:=12142336+364085=12506421, hh:=794 +176: setchar82 ("52) h:=12506421+482410=12988831, hh:=825 +177: setchar82 ("52) h:=12988831+482410=13471241, hh:=855 +178: setchar82 ("52) h:=13471241+482410=13953651, hh:=886 +179: setchar49 ("31) h:=13953651+327680=14281331, hh:=907 +180: setchar49 ("31) h:=14281331+327680=14609011, hh:=927 +181: setchar97 ("61) h:=14609011+327680=14936691, hh:=948 +182: setchar80 ("50) h:=14936691+446005=15382696, hh:=976 +183: setchar80 ("50) h:=15382696+446005=15828701, hh:=1004 +184: setchar80 ("50) h:=15828701+446005=16274706, hh:=1032 +185: setchar98 ("62) h:=16274706+364085=16638791, hh:=1055 +186: setchar80 ("50) h:=16638791+446005=17084796, hh:=1083 +187: setchar80 ("50) h:=17084796+446005=17530801, hh:=1111 +188: setchar80 ("50) h:=17530801+446005=17976806, hh:=1139 +[8a9a10aRRRbRRR11aPPPbPPP] +189: pop +level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +190: eop diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ b/Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ new file mode 100644 index 00000000000..85cd742743e --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/eup-inhibitgluea.typ @@ -0,0 +1,1846 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 3019. +maxv=43725786, maxh=30785863, maxstackdepth=4, totalpages=3 +Font 2: umin10 (JFM)---loaded at size 655360 DVI units +Font 1: cmtt10---loaded at size 655360 DVI units + +42: beginning of page 1 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: fntdef1 2: umin10 +127: fntnum2 current font is umin10 +128: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +131: fntdef1 1: cmtt10 +153: fntnum1 current font is cmtt10 +154: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +155: setchar110 ("6E) h:=974659+344061=1318720, hh:=84 +156: setchar117 ("75) h:=1318720+344061=1662781, hh:=106 +157: setchar108 ("6C) h:=1662781+344061=2006842, hh:=128 +158: setchar108 ("6C) h:=2006842+344061=2350903, hh:=150 +[\null] +159: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +160: y3 798030 v:=655360+798030=1453390, vv:=92 +164: push +level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92) +165: fntnum2 current font is umin10 +166: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +169: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +172: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +175: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +178: pop +level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92) +179: y0 798030 v:=1453390+798030=2251420, vv:=143 +180: push +level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143) +181: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +184: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +187: right3 300288 h:=960908+300288=1261196, hh:=80 +191: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +194: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +197: pop +level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143) +198: y0 798030 v:=2251420+798030=3049450, vv:=193 +199: push +level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193) +200: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +203: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +206: right3 300288 h:=960908+300288=1261196, hh:=80 +210: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +213: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +216: pop +level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193) +217: y0 798030 v:=3049450+798030=3847480, vv:=244 +218: push +level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244) +219: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +222: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +225: right3 600576 h:=960908+600576=1561484, hh:=99 +229: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +232: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +235: pop +level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244) +236: y0 798030 v:=3847480+798030=4645510, vv:=294 +237: push +level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294) +238: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +241: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +244: right3 600576 h:=960908+600576=1561484, hh:=99 +248: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +251: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +254: pop +level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294) +255: y0 798030 v:=4645510+798030=5443540, vv:=345 +256: push +level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345) +257: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +260: fntnum1 current font is cmtt10 +261: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +262: setchar117 ("75) h:=974659+344061=1318720, hh:=84 +263: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106 +264: setchar98 ("62) h:=1662781+344061=2006842, hh:=128 +265: setchar111 ("6F) h:=2006842+344061=2350903, hh:=150 +266: setchar120 ("78) h:=2350903+344061=2694964, hh:=172 +[\unbox] +267: pop +level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345) +268: y0 798030 v:=5443540+798030=6241570, vv:=395 +269: push +level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395) +270: fntnum2 current font is umin10 +271: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +274: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +277: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +280: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +283: pop +level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395) +284: y0 798030 v:=6241570+798030=7039600, vv:=446 +285: push +level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446) +286: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +289: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +292: right3 300288 h:=960908+300288=1261196, hh:=80 +296: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +299: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +302: pop +level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446) +303: y0 798030 v:=7039600+798030=7837630, vv:=496 +304: push +level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496) +305: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +308: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +311: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +314: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +317: pop +level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496) +318: y0 798030 v:=7837630+798030=8635660, vv:=547 +319: push +level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547) +320: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +323: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +326: right3 300288 h:=960908+300288=1261196, hh:=80 +330: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +333: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +336: pop +level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547) +337: y0 798030 v:=8635660+798030=9433690, vv:=598 +338: push +level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598) +339: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +342: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +345: right3 600576 h:=960908+600576=1561484, hh:=99 +349: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +352: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +355: pop +level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598) +356: y0 798030 v:=9433690+798030=10231720, vv:=648 +357: push +level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648) +358: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +361: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +364: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +367: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +370: pop +level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648) +371: z3 798031 v:=10231720+798031=11029751, vv:=699 +375: push +level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699) +376: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +379: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +382: right3 300288 h:=960908+300288=1261196, hh:=80 +386: set2 28450("6F22) type=0 h:=1261196+630598=1891794, hh:=120 +389: set2 65288("FF08) type=6 h:=1891794+330310=2222104, hh:=141 +392: set2 12354("3042) type=0 h:=2222104+630598=2852702, hh:=181 +[あ) 漢(あ] +395: pop +level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699) +396: y0 798030 v:=11029751+798030=11827781, vv:=749 +397: push +level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749) +398: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +401: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +404: set2 28450("6F22) type=0 h:=960908+630598=1591506, hh:=101 +407: right3 300288 h:=1591506+300288=1891794, hh:=120 +411: set2 65288("FF08) type=6 h:=1891794+330310=2222104, hh:=141 +414: set2 12354("3042) type=0 h:=2222104+630598=2852702, hh:=181 +[あ)漢 (あ] +417: pop +level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749) +418: y0 798030 v:=11827781+798030=12625811, vv:=800 +419: push +level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800) +420: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +423: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +426: w3 300288 h:=960908+300288=1261196, hh:=80 +430: set2 28450("6F22) type=0 h:=1261196+630598=1891794, hh:=120 +433: set2 28450("6F22) type=0 h:=1891794+630598=2522392, hh:=160 +436: w0 300288 h:=2522392+300288=2822680, hh:=179 +437: set2 65288("FF08) type=6 h:=2822680+330310=3152990, hh:=200 +440: set2 12354("3042) type=0 h:=3152990+630598=3783588, hh:=240 +[あ) 漢漢 (あ] +443: pop +level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800) +444: y0 798030 v:=12625811+798030=13423841, vv:=850 +445: push +level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850) +446: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +449: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +452: w3 300288 h:=960908+300288=1261196, hh:=80 +456: set2 28450("6F22) type=0 h:=1261196+630598=1891794, hh:=120 +459: w0 300288 h:=1891794+300288=2192082, hh:=139 +460: set2 65288("FF08) type=6 h:=2192082+330310=2522392, hh:=160 +463: set2 12354("3042) type=0 h:=2522392+630598=3152990, hh:=200 +[あ) 漢 (あ] +466: pop +level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850) +467: y0 798030 v:=13423841+798030=14221871, vv:=901 +468: push +level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901) +469: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +472: fntnum1 current font is cmtt10 +473: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +474: setchar118 ("76) h:=974659+344061=1318720, hh:=84 +475: setchar114 ("72) h:=1318720+344061=1662781, hh:=106 +476: setchar117 ("75) h:=1662781+344061=2006842, hh:=128 +477: setchar108 ("6C) h:=2006842+344061=2350903, hh:=150 +478: setchar101 ("65) h:=2350903+344061=2694964, hh:=172 +[\vrule] +479: pop +level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901) +480: y0 798030 v:=14221871+798030=15019901, vv:=951 +481: push +level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951) +482: fntnum2 current font is umin10 +483: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +486: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +489: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +492: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +495: pop +level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951) +496: y0 798030 v:=15019901+798030=15817931, vv:=1002 +497: push +level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002) +498: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +501: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +504: right3 300288 h:=960908+300288=1261196, hh:=80 +[あ) ] +508: down3 91000 v:=15817931+91000=15908931, vv:=1008 +512: setrule height 600600, width 26214 (39x2 pixels) + h:=1261196+26214=1287410, hh:=82 +521: down3 -91000 v:=15908931-91000=15817931, vv:=1002 +525: set2 65288("FF08) type=6 h:=1287410+330310=1617720, hh:=103 +528: set2 12354("3042) type=0 h:=1617720+630598=2248318, hh:=143 +[(あ] +531: pop +level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002) +532: y0 798030 v:=15817931+798030=16615961, vv:=1052 +533: push +level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052) +534: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +537: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +[あ)] +540: down3 91000 v:=16615961+91000=16706961, vv:=1058 +544: setrule height 600600, width 26214 (39x2 pixels) + h:=960908+26214=987122, hh:=63 +553: right3 300288 h:=987122+300288=1287410, hh:=82 +[ ] +557: down3 -91000 v:=16706961-91000=16615961, vv:=1052 +561: set2 65288("FF08) type=6 h:=1287410+330310=1617720, hh:=103 +564: set2 12354("3042) type=0 h:=1617720+630598=2248318, hh:=143 +[(あ] +567: pop +level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052) +568: y0 798030 v:=16615961+798030=17413991, vv:=1103 +569: push +level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103) +570: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +573: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +576: w3 300288 h:=960908+300288=1261196, hh:=80 +[あ) ] +580: down3 91000 v:=17413991+91000=17504991, vv:=1109 +584: setrule height 600600, width 26214 (39x2 pixels) + h:=1261196+26214=1287410, hh:=82 +593: setrule height 600600, width 26214 (39x2 pixels) + h:=1287410+26214=1313624, hh:=84 +602: w0 300288 h:=1313624+300288=1613912, hh:=102 +[ ] +603: down3 -91000 v:=17504991-91000=17413991, vv:=1103 +607: set2 65288("FF08) type=6 h:=1613912+330310=1944222, hh:=123 +610: set2 12354("3042) type=0 h:=1944222+630598=2574820, hh:=163 +[(あ] +613: pop +level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103) +614: y0 798030 v:=17413991+798030=18212021, vv:=1154 +615: push +level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154) +616: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +619: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +622: w3 300288 h:=960908+300288=1261196, hh:=80 +[あ) ] +626: down3 91000 v:=18212021+91000=18303021, vv:=1160 +630: setrule height 600600, width 26214 (39x2 pixels) + h:=1261196+26214=1287410, hh:=82 +639: w0 300288 h:=1287410+300288=1587698, hh:=101 +[ ] +640: down3 -91000 v:=18303021-91000=18212021, vv:=1154 +644: set2 65288("FF08) type=6 h:=1587698+330310=1918008, hh:=122 +647: set2 12354("3042) type=0 h:=1918008+630598=2548606, hh:=162 +[(あ] +650: pop +level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154) +651: y0 798030 v:=18212021+798030=19010051, vv:=1204 +652: push +level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204) +653: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +656: fntnum1 current font is cmtt10 +657: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +658: setchar100 ("64) h:=974659+344061=1318720, hh:=84 +659: setchar105 ("69) h:=1318720+344061=1662781, hh:=106 +660: setchar115 ("73) h:=1662781+344061=2006842, hh:=128 +661: setchar99 ("63) h:=2006842+344061=2350903, hh:=150 +662: setchar114 ("72) h:=2350903+344061=2694964, hh:=172 +663: setchar101 ("65) h:=2694964+344061=3039025, hh:=194 +664: setchar116 ("74) h:=3039025+344061=3383086, hh:=216 +665: setchar105 ("69) h:=3383086+344061=3727147, hh:=238 +666: setchar111 ("6F) h:=3727147+344061=4071208, hh:=260 +667: setchar110 ("6E) h:=4071208+344061=4415269, hh:=282 +668: setchar97 ("61) h:=4415269+344061=4759330, hh:=303 +669: setchar114 ("72) h:=4759330+344061=5103391, hh:=325 +670: setchar121 ("79) h:=5103391+344061=5447452, hh:=347 +[\discretionary] +671: pop +level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204) +672: y0 798030 v:=19010051+798030=19808081, vv:=1255 +673: push +level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255) +674: fntnum2 current font is umin10 +675: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +678: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +681: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +684: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +687: pop +level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255) +688: y0 798030 v:=19808081+798030=20606111, vv:=1305 +689: push +level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305) +690: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +693: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +696: right3 300288 h:=960908+300288=1261196, hh:=80 +700: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +703: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +706: pop +level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305) +707: y0 798030 v:=20606111+798030=21404141, vv:=1356 +708: push +level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356) +709: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +712: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +715: right3 300288 h:=960908+300288=1261196, hh:=80 +719: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +722: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +725: pop +level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356) +726: y0 798030 v:=21404141+798030=22202171, vv:=1406 +727: push +level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406) +728: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +731: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +734: right3 600576 h:=960908+600576=1561484, hh:=99 +738: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +741: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +744: pop +level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406) +745: y0 798030 v:=22202171+798030=23000201, vv:=1457 +746: push +level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457) +747: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +750: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +753: right3 600576 h:=960908+600576=1561484, hh:=99 +757: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +760: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +763: pop +level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457) +764: y0 798030 v:=23000201+798030=23798231, vv:=1507 +765: push +level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507) +766: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +769: fntnum1 current font is cmtt10 +770: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +771: setchar115 ("73) h:=974659+344061=1318720, hh:=84 +772: setchar112 ("70) h:=1318720+344061=1662781, hh:=106 +773: setchar101 ("65) h:=1662781+344061=2006842, hh:=128 +774: setchar99 ("63) h:=2006842+344061=2350903, hh:=150 +775: setchar105 ("69) h:=2350903+344061=2694964, hh:=172 +776: setchar97 ("61) h:=2694964+344061=3039025, hh:=194 +777: setchar108 ("6C) h:=3039025+344061=3383086, hh:=216 +[\special] +778: pop +level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507) +779: y0 798030 v:=23798231+798030=24596261, vv:=1558 +780: push +level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558) +781: fntnum2 current font is umin10 +782: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +785: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +788: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +791: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +794: pop +level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558) +795: y0 798030 v:=24596261+798030=25394291, vv:=1608 +796: push +level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608) +797: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +800: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +803: right3 300288 h:=960908+300288=1261196, hh:=80 +[あ) ] +807: xxx '' +809: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +812: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[(あ] +815: pop +level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608) +816: y0 798030 v:=25394291+798030=26192321, vv:=1659 +817: push +level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659) +818: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +821: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +[あ)] +824: xxx '' +826: right3 300288 h:=960908+300288=1261196, hh:=80 +830: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +833: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[ (あ] +836: pop +level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659) +837: y0 798030 v:=26192321+798030=26990351, vv:=1710 +838: push +level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710) +839: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +842: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +845: w3 300288 h:=960908+300288=1261196, hh:=80 +[あ) ] +849: xxx '' +851: xxx '' +853: w0 300288 h:=1261196+300288=1561484, hh:=99 +854: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +857: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[ (あ] +860: pop +level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710) +861: y0 798030 v:=26990351+798030=27788381, vv:=1760 +862: push +level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760) +863: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +866: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +869: w3 300288 h:=960908+300288=1261196, hh:=80 +[あ) ] +873: xxx '' +875: w0 300288 h:=1261196+300288=1561484, hh:=99 +876: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +879: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[ (あ] +882: pop +level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760) +883: y0 798030 v:=27788381+798030=28586411, vv:=1811 +884: push +level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811) +885: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +888: fntnum1 current font is cmtt10 +889: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +890: setchar119 ("77) h:=974659+344061=1318720, hh:=84 +891: setchar114 ("72) h:=1318720+344061=1662781, hh:=106 +892: setchar105 ("69) h:=1662781+344061=2006842, hh:=128 +893: setchar116 ("74) h:=2006842+344061=2350903, hh:=150 +894: setchar101 ("65) h:=2350903+344061=2694964, hh:=172 +895: setchar45 ("2D) h:=2694964+344061=3039025, hh:=194 +896: setchar49 ("31) h:=3039025+344061=3383086, hh:=216 +[\write-1] +897: pop +level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811) +898: y0 798030 v:=28586411+798030=29384441, vv:=1861 +899: push +level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861) +900: fntnum2 current font is umin10 +901: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +904: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +907: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +910: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +913: pop +level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861) +914: y0 798030 v:=29384441+798030=30182471, vv:=1912 +915: push +level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912) +916: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +919: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +922: right3 300288 h:=960908+300288=1261196, hh:=80 +926: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +929: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +932: pop +level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912) +933: y0 798030 v:=30182471+798030=30980501, vv:=1962 +934: push +level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962) +935: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +938: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +941: right3 300288 h:=960908+300288=1261196, hh:=80 +945: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +948: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +951: pop +level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962) +952: z0 798031 v:=30980501+798031=31778532, vv:=2013 +953: push +level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013) +954: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +957: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +960: right3 600576 h:=960908+600576=1561484, hh:=99 +964: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +967: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +970: pop +level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013) +971: y0 798030 v:=31778532+798030=32576562, vv:=2063 +972: push +level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063) +973: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +976: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +979: right3 600576 h:=960908+600576=1561484, hh:=99 +983: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +986: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +989: pop +level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063) +990: y0 798030 v:=32576562+798030=33374592, vv:=2114 +991: push +level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114) +992: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +995: fntnum1 current font is cmtt10 +996: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +997: setchar105 ("69) h:=974659+344061=1318720, hh:=84 +998: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106 +999: setchar115 ("73) h:=1662781+344061=2006842, hh:=128 +1000: setchar101 ("65) h:=2006842+344061=2350903, hh:=150 +1001: setchar114 ("72) h:=2350903+344061=2694964, hh:=172 +1002: setchar116 ("74) h:=2694964+344061=3039025, hh:=194 +[\insert] +1003: pop +level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114) +1004: y0 798030 v:=33374592+798030=34172622, vv:=2165 +1005: push +level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165) +1006: fntnum2 current font is umin10 +1007: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1010: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1013: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1016: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1019: pop +level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165) +1020: y0 798030 v:=34172622+798030=34970652, vv:=2215 +1021: push +level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215) +1022: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1025: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1028: right3 300288 h:=960908+300288=1261196, hh:=80 +1032: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1035: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1038: pop +level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215) +1039: y0 798030 v:=34970652+798030=35768682, vv:=2266 +1040: push +level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266) +1041: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1044: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1047: right3 300288 h:=960908+300288=1261196, hh:=80 +1051: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1054: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1057: pop +level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266) +1058: y0 798030 v:=35768682+798030=36566712, vv:=2316 +1059: push +level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316) +1060: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1063: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1066: right3 600576 h:=960908+600576=1561484, hh:=99 +1070: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1073: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1076: pop +level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316) +1077: y0 798030 v:=36566712+798030=37364742, vv:=2367 +1078: push +level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367) +1079: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1082: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1085: right3 600576 h:=960908+600576=1561484, hh:=99 +1089: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1092: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1095: pop +level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367) +1096: y0 798030 v:=37364742+798030=38162772, vv:=2417 +1097: push +level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417) +1098: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1101: fntnum1 current font is cmtt10 +1102: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1103: setchar109 ("6D) h:=974659+344061=1318720, hh:=84 +1104: setchar97 ("61) h:=1318720+344061=1662781, hh:=106 +1105: setchar114 ("72) h:=1662781+344061=2006842, hh:=128 +1106: setchar107 ("6B) h:=2006842+344061=2350903, hh:=150 +[\mark] +1107: pop +level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417) +1108: y0 798030 v:=38162772+798030=38960802, vv:=2468 +1109: push +level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468) +1110: fntnum2 current font is umin10 +1111: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1114: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1117: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1120: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1123: pop +level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468) +1124: y0 798030 v:=38960802+798030=39758832, vv:=2518 +1125: push +level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518) +1126: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1129: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1132: right3 300288 h:=960908+300288=1261196, hh:=80 +1136: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1139: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1142: pop +level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518) +1143: y0 798030 v:=39758832+798030=40556862, vv:=2569 +1144: push +level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569) +1145: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1148: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1151: right3 300288 h:=960908+300288=1261196, hh:=80 +1155: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1158: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1161: pop +level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569) +1162: y0 798030 v:=40556862+798030=41354892, vv:=2619 +1163: push +level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619) +1164: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1167: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1170: right3 600576 h:=960908+600576=1561484, hh:=99 +1174: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1177: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1180: pop +level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619) +1181: y0 798030 v:=41354892+798030=42152922, vv:=2670 +1182: push +level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670) +1183: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1186: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1189: right3 600576 h:=960908+600576=1561484, hh:=99 +1193: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1196: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1199: pop +level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670) +1200: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1201: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +1205: eop + +1206: beginning of page 2 +1251: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +1252: down3 -917504 v:=0-917504=-917504, vv:=-58 +1256: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +1257: down4 42152922 v:=0+42152922=42152922, vv:=2670 +1262: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1263: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +1268: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +1269: fntnum2 current font is umin10 +1270: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1273: fntnum1 current font is cmtt10 +1274: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1275: setchar118 ("76) h:=974659+344061=1318720, hh:=84 +1276: setchar97 ("61) h:=1318720+344061=1662781, hh:=106 +1277: setchar100 ("64) h:=1662781+344061=2006842, hh:=128 +1278: setchar106 ("6A) h:=2006842+344061=2350903, hh:=150 +1279: setchar117 ("75) h:=2350903+344061=2694964, hh:=172 +1280: setchar115 ("73) h:=2694964+344061=3039025, hh:=194 +1281: setchar116 ("74) h:=3039025+344061=3383086, hh:=216 +[\vadjust] +1282: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +1283: y3 798030 v:=655360+798030=1453390, vv:=92 +1287: push +level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92) +1288: fntnum2 current font is umin10 +1289: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1292: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1295: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1298: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1301: pop +level 1:(h=0,v=1453390,w=0,x=0,y=798030,z=0,hh=0,vv=92) +1302: y0 798030 v:=1453390+798030=2251420, vv:=143 +1303: push +level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143) +1304: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1307: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1310: right3 300288 h:=960908+300288=1261196, hh:=80 +1314: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1317: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1320: pop +level 1:(h=0,v=2251420,w=0,x=0,y=798030,z=0,hh=0,vv=143) +1321: y0 798030 v:=2251420+798030=3049450, vv:=193 +1322: push +level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193) +1323: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1326: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1329: right3 300288 h:=960908+300288=1261196, hh:=80 +1333: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1336: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1339: pop +level 1:(h=0,v=3049450,w=0,x=0,y=798030,z=0,hh=0,vv=193) +1340: y0 798030 v:=3049450+798030=3847480, vv:=244 +1341: push +level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244) +1342: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1345: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1348: right3 600576 h:=960908+600576=1561484, hh:=99 +1352: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1355: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1358: pop +level 1:(h=0,v=3847480,w=0,x=0,y=798030,z=0,hh=0,vv=244) +1359: y0 798030 v:=3847480+798030=4645510, vv:=294 +1360: push +level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294) +1361: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1364: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1367: right3 600576 h:=960908+600576=1561484, hh:=99 +1371: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1374: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1377: pop +level 1:(h=0,v=4645510,w=0,x=0,y=798030,z=0,hh=0,vv=294) +1378: y0 798030 v:=4645510+798030=5443540, vv:=345 +1379: push +level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345) +1380: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1383: fntnum1 current font is cmtt10 +1384: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1385: setchar104 ("68) h:=974659+344061=1318720, hh:=84 +1386: setchar115 ("73) h:=1318720+344061=1662781, hh:=106 +1387: setchar107 ("6B) h:=1662781+344061=2006842, hh:=128 +1388: setchar105 ("69) h:=2006842+344061=2350903, hh:=150 +1389: setchar112 ("70) h:=2350903+344061=2694964, hh:=172 +1390: setchar48 ("30) h:=2694964+344061=3039025, hh:=194 +1391: setchar112 ("70) h:=3039025+344061=3383086, hh:=216 +1392: setchar116 ("74) h:=3383086+344061=3727147, hh:=238 +[\hskip0pt] +1393: pop +level 1:(h=0,v=5443540,w=0,x=0,y=798030,z=0,hh=0,vv=345) +1394: y0 798030 v:=5443540+798030=6241570, vv:=395 +1395: push +level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395) +1396: fntnum2 current font is umin10 +1397: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1400: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1403: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1406: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1409: pop +level 1:(h=0,v=6241570,w=0,x=0,y=798030,z=0,hh=0,vv=395) +1410: y0 798030 v:=6241570+798030=7039600, vv:=446 +1411: push +level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446) +1412: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1415: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1418: right3 300288 h:=960908+300288=1261196, hh:=80 +1422: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1425: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1428: pop +level 1:(h=0,v=7039600,w=0,x=0,y=798030,z=0,hh=0,vv=446) +1429: y0 798030 v:=7039600+798030=7837630, vv:=496 +1430: push +level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496) +1431: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1434: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1437: right3 300288 h:=960908+300288=1261196, hh:=80 +1441: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1444: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1447: pop +level 1:(h=0,v=7837630,w=0,x=0,y=798030,z=0,hh=0,vv=496) +1448: y0 798030 v:=7837630+798030=8635660, vv:=547 +1449: push +level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547) +1450: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1453: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1456: right3 600576 h:=960908+600576=1561484, hh:=99 +1460: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1463: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1466: pop +level 1:(h=0,v=8635660,w=0,x=0,y=798030,z=0,hh=0,vv=547) +1467: y0 798030 v:=8635660+798030=9433690, vv:=598 +1468: push +level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598) +1469: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1472: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1475: right3 600576 h:=960908+600576=1561484, hh:=99 +1479: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1482: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1485: pop +level 1:(h=0,v=9433690,w=0,x=0,y=798030,z=0,hh=0,vv=598) +1486: y0 798030 v:=9433690+798030=10231720, vv:=648 +1487: push +level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648) +1488: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1491: fntnum1 current font is cmtt10 +1492: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1493: setchar107 ("6B) h:=974659+344061=1318720, hh:=84 +1494: setchar101 ("65) h:=1318720+344061=1662781, hh:=106 +1495: setchar114 ("72) h:=1662781+344061=2006842, hh:=128 +1496: setchar110 ("6E) h:=2006842+344061=2350903, hh:=150 +1497: setchar48 ("30) h:=2350903+344061=2694964, hh:=172 +1498: setchar112 ("70) h:=2694964+344061=3039025, hh:=194 +1499: setchar116 ("74) h:=3039025+344061=3383086, hh:=216 +[\kern0pt] +1500: pop +level 1:(h=0,v=10231720,w=0,x=0,y=798030,z=0,hh=0,vv=648) +1501: z3 798031 v:=10231720+798031=11029751, vv:=699 +1505: push +level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699) +1506: fntnum2 current font is umin10 +1507: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1510: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1513: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1516: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1519: pop +level 1:(h=0,v=11029751,w=0,x=0,y=798030,z=798031,hh=0,vv=699) +1520: y0 798030 v:=11029751+798030=11827781, vv:=749 +1521: push +level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749) +1522: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1525: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1528: right3 300288 h:=960908+300288=1261196, hh:=80 +1532: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1535: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1538: pop +level 1:(h=0,v=11827781,w=0,x=0,y=798030,z=798031,hh=0,vv=749) +1539: y0 798030 v:=11827781+798030=12625811, vv:=800 +1540: push +level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800) +1541: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1544: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1547: right3 300288 h:=960908+300288=1261196, hh:=80 +1551: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1554: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1557: pop +level 1:(h=0,v=12625811,w=0,x=0,y=798030,z=798031,hh=0,vv=800) +1558: y0 798030 v:=12625811+798030=13423841, vv:=850 +1559: push +level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850) +1560: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1563: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1566: right3 600576 h:=960908+600576=1561484, hh:=99 +1570: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1573: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1576: pop +level 1:(h=0,v=13423841,w=0,x=0,y=798030,z=798031,hh=0,vv=850) +1577: y0 798030 v:=13423841+798030=14221871, vv:=901 +1578: push +level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901) +1579: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1582: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1585: right3 600576 h:=960908+600576=1561484, hh:=99 +1589: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1592: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1595: pop +level 1:(h=0,v=14221871,w=0,x=0,y=798030,z=798031,hh=0,vv=901) +1596: y0 798030 v:=14221871+798030=15019901, vv:=951 +1597: push +level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951) +1598: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1601: fntnum1 current font is cmtt10 +1602: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1603: setchar112 ("70) h:=974659+344061=1318720, hh:=84 +1604: setchar101 ("65) h:=1318720+344061=1662781, hh:=106 +1605: setchar110 ("6E) h:=1662781+344061=2006842, hh:=128 +1606: setchar97 ("61) h:=2006842+344061=2350903, hh:=150 +1607: setchar108 ("6C) h:=2350903+344061=2694964, hh:=172 +1608: setchar116 ("74) h:=2694964+344061=3039025, hh:=194 +1609: setchar121 ("79) h:=3039025+344061=3383086, hh:=216 +1610: setchar49 ("31) h:=3383086+344061=3727147, hh:=238 +1611: setchar48 ("30) h:=3727147+344061=4071208, hh:=260 +1612: setchar48 ("30) h:=4071208+344061=4415269, hh:=282 +1613: setchar48 ("30) h:=4415269+344061=4759330, hh:=303 +1614: setchar48 ("30) h:=4759330+344061=5103391, hh:=325 +[\penalty10000] +1615: pop +level 1:(h=0,v=15019901,w=0,x=0,y=798030,z=798031,hh=0,vv=951) +1616: y0 798030 v:=15019901+798030=15817931, vv:=1002 +1617: push +level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002) +1618: fntnum2 current font is umin10 +1619: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1622: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1625: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1628: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1631: pop +level 1:(h=0,v=15817931,w=0,x=0,y=798030,z=798031,hh=0,vv=1002) +1632: y0 798030 v:=15817931+798030=16615961, vv:=1052 +1633: push +level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052) +1634: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1637: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1640: right3 300288 h:=960908+300288=1261196, hh:=80 +1644: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1647: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1650: pop +level 1:(h=0,v=16615961,w=0,x=0,y=798030,z=798031,hh=0,vv=1052) +1651: y0 798030 v:=16615961+798030=17413991, vv:=1103 +1652: push +level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103) +1653: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1656: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1659: right3 300288 h:=960908+300288=1261196, hh:=80 +1663: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1666: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1669: pop +level 1:(h=0,v=17413991,w=0,x=0,y=798030,z=798031,hh=0,vv=1103) +1670: y0 798030 v:=17413991+798030=18212021, vv:=1154 +1671: push +level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154) +1672: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1675: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1678: right3 600576 h:=960908+600576=1561484, hh:=99 +1682: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1685: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1688: pop +level 1:(h=0,v=18212021,w=0,x=0,y=798030,z=798031,hh=0,vv=1154) +1689: y0 798030 v:=18212021+798030=19010051, vv:=1204 +1690: push +level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204) +1691: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1694: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1697: right3 600576 h:=960908+600576=1561484, hh:=99 +1701: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1704: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1707: pop +level 1:(h=0,v=19010051,w=0,x=0,y=798030,z=798031,hh=0,vv=1204) +1708: y0 798030 v:=19010051+798030=19808081, vv:=1255 +1709: push +level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255) +1710: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1713: fntnum1 current font is cmtt10 +1714: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1715: setchar114 ("72) h:=974659+344061=1318720, hh:=84 +1716: setchar101 ("65) h:=1318720+344061=1662781, hh:=106 +1717: setchar108 ("6C) h:=1662781+344061=2006842, hh:=128 +1718: setchar97 ("61) h:=2006842+344061=2350903, hh:=150 +1719: setchar120 ("78) h:=2350903+344061=2694964, hh:=172 +[\relax] +1720: pop +level 1:(h=0,v=19808081,w=0,x=0,y=798030,z=798031,hh=0,vv=1255) +1721: y0 798030 v:=19808081+798030=20606111, vv:=1305 +1722: push +level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305) +1723: fntnum2 current font is umin10 +1724: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1727: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1730: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1733: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1736: pop +level 1:(h=0,v=20606111,w=0,x=0,y=798030,z=798031,hh=0,vv=1305) +1737: y0 798030 v:=20606111+798030=21404141, vv:=1356 +1738: push +level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356) +1739: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1742: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1745: right3 300288 h:=960908+300288=1261196, hh:=80 +1749: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1752: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1755: pop +level 1:(h=0,v=21404141,w=0,x=0,y=798030,z=798031,hh=0,vv=1356) +1756: y0 798030 v:=21404141+798030=22202171, vv:=1406 +1757: push +level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406) +1758: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1761: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1764: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1767: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1770: pop +level 1:(h=0,v=22202171,w=0,x=0,y=798030,z=798031,hh=0,vv=1406) +1771: y0 798030 v:=22202171+798030=23000201, vv:=1457 +1772: push +level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457) +1773: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1776: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1779: right3 300288 h:=960908+300288=1261196, hh:=80 +1783: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1786: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1789: pop +level 1:(h=0,v=23000201,w=0,x=0,y=798030,z=798031,hh=0,vv=1457) +1790: y0 798030 v:=23000201+798030=23798231, vv:=1507 +1791: push +level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507) +1792: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1795: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1798: right3 600576 h:=960908+600576=1561484, hh:=99 +1802: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1805: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1808: pop +level 1:(h=0,v=23798231,w=0,x=0,y=798030,z=798031,hh=0,vv=1507) +1809: y0 798030 v:=23798231+798030=24596261, vv:=1558 +1810: push +level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558) +1811: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1814: set2 12354("3042) type=0 h:=630598+630598=1261196, hh:=80 +1817: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1820: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[ああ(あ] +1823: pop +level 1:(h=0,v=24596261,w=0,x=0,y=798030,z=798031,hh=0,vv=1558) +1824: y0 798030 v:=24596261+798030=25394291, vv:=1608 +1825: push +level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608) +1826: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1829: set2 12354("3042) type=0 h:=630598+630598=1261196, hh:=80 +1832: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1835: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[ああ(あ] +1838: pop +level 1:(h=0,v=25394291,w=0,x=0,y=798030,z=798031,hh=0,vv=1608) +1839: y0 798030 v:=25394291+798030=26192321, vv:=1659 +1840: push +level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659) +1841: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1844: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1847: right3 300288 h:=960908+300288=1261196, hh:=80 +1851: set2 12354("3042) type=0 h:=1261196+630598=1891794, hh:=120 +1854: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) ああ] +1857: pop +level 1:(h=0,v=26192321,w=0,x=0,y=798030,z=798031,hh=0,vv=1659) +1858: y0 798030 v:=26192321+798030=26990351, vv:=1710 +1859: push +level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710) +1860: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1863: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1866: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101 +1869: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ)ああ] +1872: pop +level 1:(h=0,v=26990351,w=0,x=0,y=798030,z=798031,hh=0,vv=1710) +1873: y0 798030 v:=26990351+798030=27788381, vv:=1760 +1874: push +level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760) +1875: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1878: fntnum1 current font is cmtt10 +1879: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1880: setchar105 ("69) h:=974659+344061=1318720, hh:=84 +1881: setchar103 ("67) h:=1318720+344061=1662781, hh:=106 +1882: setchar110 ("6E) h:=1662781+344061=2006842, hh:=128 +1883: setchar111 ("6F) h:=2006842+344061=2350903, hh:=150 +1884: setchar114 ("72) h:=2350903+344061=2694964, hh:=172 +1885: setchar101 ("65) h:=2694964+344061=3039025, hh:=194 +1886: setchar115 ("73) h:=3039025+344061=3383086, hh:=216 +1887: setchar112 ("70) h:=3383086+344061=3727147, hh:=238 +1888: setchar97 ("61) h:=3727147+344061=4071208, hh:=260 +1889: setchar99 ("63) h:=4071208+344061=4415269, hh:=282 +1890: setchar101 ("65) h:=4415269+344061=4759330, hh:=303 +1891: setchar115 ("73) h:=4759330+344061=5103391, hh:=325 +[\ignorespaces] +1892: pop +level 1:(h=0,v=27788381,w=0,x=0,y=798030,z=798031,hh=0,vv=1760) +1893: y0 798030 v:=27788381+798030=28586411, vv:=1811 +1894: push +level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811) +1895: fntnum2 current font is umin10 +1896: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1899: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1902: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1905: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1908: pop +level 1:(h=0,v=28586411,w=0,x=0,y=798030,z=798031,hh=0,vv=1811) +1909: y0 798030 v:=28586411+798030=29384441, vv:=1861 +1910: push +level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861) +1911: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1914: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1917: right3 300288 h:=960908+300288=1261196, hh:=80 +1921: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1924: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1927: pop +level 1:(h=0,v=29384441,w=0,x=0,y=798030,z=798031,hh=0,vv=1861) +1928: y0 798030 v:=29384441+798030=30182471, vv:=1912 +1929: push +level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912) +1930: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1933: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1936: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +1939: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +1942: pop +level 1:(h=0,v=30182471,w=0,x=0,y=798030,z=798031,hh=0,vv=1912) +1943: y0 798030 v:=30182471+798030=30980501, vv:=1962 +1944: push +level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962) +1945: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1948: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1951: right3 300288 h:=960908+300288=1261196, hh:=80 +1955: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +1958: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +1961: pop +level 1:(h=0,v=30980501,w=0,x=0,y=798030,z=798031,hh=0,vv=1962) +1962: z0 798031 v:=30980501+798031=31778532, vv:=2013 +1963: push +level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013) +1964: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +1967: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +1970: right3 600576 h:=960908+600576=1561484, hh:=99 +1974: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +1977: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +1980: pop +level 1:(h=0,v=31778532,w=0,x=0,y=798030,z=798031,hh=0,vv=2013) +1981: y0 798030 v:=31778532+798030=32576562, vv:=2063 +1982: push +level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063) +1983: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +1986: fntnum1 current font is cmtt10 +1987: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +1988: setchar115 ("73) h:=974659+344061=1318720, hh:=84 +1989: setchar112 ("70) h:=1318720+344061=1662781, hh:=106 +1990: setchar97 ("61) h:=1662781+344061=2006842, hh:=128 +1991: setchar99 ("63) h:=2006842+344061=2350903, hh:=150 +1992: setchar101 ("65) h:=2350903+344061=2694964, hh:=172 +1993: setchar102 ("66) h:=2694964+344061=3039025, hh:=194 +1994: setchar97 ("61) h:=3039025+344061=3383086, hh:=216 +1995: setchar99 ("63) h:=3383086+344061=3727147, hh:=238 +1996: setchar116 ("74) h:=3727147+344061=4071208, hh:=260 +1997: setchar111 ("6F) h:=4071208+344061=4415269, hh:=282 +1998: setchar114 ("72) h:=4415269+344061=4759330, hh:=303 +1999: setchar49 ("31) h:=4759330+344061=5103391, hh:=325 +2000: setchar48 ("30) h:=5103391+344061=5447452, hh:=347 +2001: setchar48 ("30) h:=5447452+344061=5791513, hh:=369 +2002: setchar48 ("30) h:=5791513+344061=6135574, hh:=391 +[\spacefactor1000] +2003: pop +level 1:(h=0,v=32576562,w=0,x=0,y=798030,z=798031,hh=0,vv=2063) +2004: y0 798030 v:=32576562+798030=33374592, vv:=2114 +2005: push +level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114) +2006: fntnum2 current font is umin10 +2007: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2010: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2013: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +2016: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +2019: pop +level 1:(h=0,v=33374592,w=0,x=0,y=798030,z=798031,hh=0,vv=2114) +2020: y0 798030 v:=33374592+798030=34172622, vv:=2165 +2021: push +level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165) +2022: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2025: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2028: right3 300288 h:=960908+300288=1261196, hh:=80 +2032: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2035: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2038: pop +level 1:(h=0,v=34172622,w=0,x=0,y=798030,z=798031,hh=0,vv=2165) +2039: y0 798030 v:=34172622+798030=34970652, vv:=2215 +2040: push +level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215) +2041: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2044: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2047: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +2050: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +2053: pop +level 1:(h=0,v=34970652,w=0,x=0,y=798030,z=798031,hh=0,vv=2215) +2054: y0 798030 v:=34970652+798030=35768682, vv:=2266 +2055: push +level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266) +2056: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2059: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2062: right3 300288 h:=960908+300288=1261196, hh:=80 +2066: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2069: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2072: pop +level 1:(h=0,v=35768682,w=0,x=0,y=798030,z=798031,hh=0,vv=2266) +2073: y0 798030 v:=35768682+798030=36566712, vv:=2316 +2074: push +level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316) +2075: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2078: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2081: right3 600576 h:=960908+600576=1561484, hh:=99 +2085: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +2088: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +2091: pop +level 1:(h=0,v=36566712,w=0,x=0,y=798030,z=798031,hh=0,vv=2316) +2092: y0 798030 v:=36566712+798030=37364742, vv:=2367 +2093: push +level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367) +2094: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +2097: fntnum1 current font is cmtt10 +2098: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +2099: setchar108 ("6C) h:=974659+344061=1318720, hh:=84 +2100: setchar101 ("65) h:=1318720+344061=1662781, hh:=106 +2101: setchar116 ("74) h:=1662781+344061=2006842, hh:=128 +[\let] +2102: pop +level 1:(h=0,v=37364742,w=0,x=0,y=798030,z=798031,hh=0,vv=2367) +2103: y0 798030 v:=37364742+798030=38162772, vv:=2417 +2104: push +level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417) +2105: fntnum2 current font is umin10 +2106: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2109: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2112: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +2115: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +2118: pop +level 1:(h=0,v=38162772,w=0,x=0,y=798030,z=798031,hh=0,vv=2417) +2119: y0 798030 v:=38162772+798030=38960802, vv:=2468 +2120: push +level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468) +2121: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2124: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2127: right3 300288 h:=960908+300288=1261196, hh:=80 +2131: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2134: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2137: pop +level 1:(h=0,v=38960802,w=0,x=0,y=798030,z=798031,hh=0,vv=2468) +2138: y0 798030 v:=38960802+798030=39758832, vv:=2518 +2139: push +level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518) +2140: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2143: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2146: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +2149: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +2152: pop +level 1:(h=0,v=39758832,w=0,x=0,y=798030,z=798031,hh=0,vv=2518) +2153: y0 798030 v:=39758832+798030=40556862, vv:=2569 +2154: push +level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569) +2155: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2158: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2161: right3 300288 h:=960908+300288=1261196, hh:=80 +2165: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2168: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2171: pop +level 1:(h=0,v=40556862,w=0,x=0,y=798030,z=798031,hh=0,vv=2569) +2172: y0 798030 v:=40556862+798030=41354892, vv:=2619 +2173: push +level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619) +2174: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2177: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2180: right3 600576 h:=960908+600576=1561484, hh:=99 +2184: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +2187: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +2190: pop +level 1:(h=0,v=41354892,w=0,x=0,y=798030,z=798031,hh=0,vv=2619) +2191: y0 798030 v:=41354892+798030=42152922, vv:=2670 +2192: push +level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670) +2193: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +2196: fntnum1 current font is cmtt10 +2197: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +2198: setchar103 ("67) h:=974659+344061=1318720, hh:=84 +2199: setchar114 ("72) h:=1318720+344061=1662781, hh:=106 +2200: setchar111 ("6F) h:=1662781+344061=2006842, hh:=128 +2201: setchar117 ("75) h:=2006842+344061=2350903, hh:=150 +2202: setchar112 ("70) h:=2350903+344061=2694964, hh:=172 +[\group] +2203: pop +level 1:(h=0,v=42152922,w=0,x=0,y=798030,z=798031,hh=0,vv=2670) +2204: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +2205: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +2209: eop + +2210: beginning of page 3 +2255: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +2256: down3 -917504 v:=0-917504=-917504, vv:=-58 +2260: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +2261: down4 42152922 v:=0+42152922=42152922, vv:=2670 +2266: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +2267: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +2272: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +2273: fntnum2 current font is umin10 +2274: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2277: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2280: right3 300288 h:=960908+300288=1261196, hh:=80 +2284: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2287: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2290: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +2291: y3 786432 v:=655360+786432=1441792, vv:=91 +2295: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +2296: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2299: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2302: right3 600576 h:=960908+600576=1561484, hh:=99 +2306: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +2309: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) (あ] +2312: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +2313: y0 786432 v:=1441792+786432=2228224, vv:=141 +2314: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +2315: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2318: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2321: right3 300288 h:=960908+300288=1261196, hh:=80 +2325: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2328: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2331: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +2332: y0 786432 v:=2228224+786432=3014656, vv:=191 +2333: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +2334: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2337: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2340: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +2343: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +2346: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +2347: y0 786432 v:=3014656+786432=3801088, vv:=241 +2348: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +2349: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2352: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2355: right3 300288 h:=960908+300288=1261196, hh:=80 +2359: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2362: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2365: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +2366: y0 786432 v:=3801088+786432=4587520, vv:=291 +2367: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +2368: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2371: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2374: right3 300288 h:=960908+300288=1261196, hh:=80 +2378: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2381: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ) (あ] +2384: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +2385: y0 786432 v:=4587520+786432=5373952, vv:=340 +2386: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +2387: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2390: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2393: set2 65288("FF08) type=6 h:=960908+330310=1291218, hh:=82 +2396: set2 12354("3042) type=0 h:=1291218+630598=1921816, hh:=122 +[あ)(あ] +2399: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +2400: y0 786432 v:=5373952+786432=6160384, vv:=390 +2401: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +2402: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2405: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2408: right3 150143 h:=960908+150143=1111051, hh:=70 +2412: set2 12539("30FB) type=4 h:=1111051+231778=1342829, hh:=85 +2415: right3 199410 h:=1342829+199410=1542239, hh:=98 +2419: set2 12354("3042) type=0 h:=1542239+630598=2172837, hh:=138 +[あ) ・ あ] +2422: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +2423: y0 786432 v:=6160384+786432=6946816, vv:=440 +2424: push +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +2425: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2428: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2431: right3 499698 h:=960908+499698=1460606, hh:=93 +2435: set2 12539("30FB) type=4 h:=1460606+231778=1692384, hh:=108 +2438: right3 199410 h:=1692384+199410=1891794, hh:=120 +2442: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ) ・ あ] +2445: pop +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +2446: y0 786432 v:=6946816+786432=7733248, vv:=490 +2447: push +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +2448: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2451: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2454: right3 300288 h:=960908+300288=1261196, hh:=80 +2458: set2 12539("30FB) type=4 h:=1261196+231778=1492974, hh:=95 +2461: right3 199410 h:=1492974+199410=1692384, hh:=107 +2465: set2 12354("3042) type=0 h:=1692384+630598=2322982, hh:=147 +[あ) ・ あ] +2468: pop +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +2469: y0 786432 v:=7733248+786432=8519680, vv:=540 +2470: push +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +2471: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2474: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2477: set2 12539("30FB) type=4 h:=960908+231778=1192686, hh:=76 +2480: right3 199410 h:=1192686+199410=1392096, hh:=88 +2484: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=128 +[あ)・ あ] +2487: pop +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +2488: y0 786432 v:=8519680+786432=9306112, vv:=589 +2489: push +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +2490: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2493: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2496: right3 300288 h:=960908+300288=1261196, hh:=80 +2500: set2 12539("30FB) type=4 h:=1261196+231778=1492974, hh:=95 +2503: right3 199410 h:=1492974+199410=1692384, hh:=107 +2507: set2 12354("3042) type=0 h:=1692384+630598=2322982, hh:=147 +[あ) ・ あ] +2510: pop +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +2511: y0 786432 v:=9306112+786432=10092544, vv:=639 +2512: push +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +2513: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2516: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2519: right3 300288 h:=960908+300288=1261196, hh:=80 +2523: set2 12539("30FB) type=4 h:=1261196+231778=1492974, hh:=95 +2526: right3 199410 h:=1492974+199410=1692384, hh:=107 +2530: set2 12354("3042) type=0 h:=1692384+630598=2322982, hh:=147 +[あ) ・ あ] +2533: pop +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +2534: y0 786432 v:=10092544+786432=10878976, vv:=689 +2535: push +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +2536: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2539: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2542: set2 12539("30FB) type=4 h:=960908+231778=1192686, hh:=76 +2545: right3 199410 h:=1192686+199410=1392096, hh:=88 +2549: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=128 +[あ)・ あ] +2552: pop +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +2553: y0 786432 v:=10878976+786432=11665408, vv:=739 +2554: push +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +2555: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2558: right3 199410 h:=630598+199410=830008, hh:=53 +2562: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2565: right3 150143 h:=1061786+150143=1211929, hh:=77 +2569: set2 65288("FF08) type=6 h:=1211929+330310=1542239, hh:=98 +2572: set2 12354("3042) type=0 h:=1542239+630598=2172837, hh:=138 +[あ ・ (あ] +2575: pop +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +2576: y0 786432 v:=11665408+786432=12451840, vv:=789 +2577: push +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +2578: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2581: right3 199410 h:=630598+199410=830008, hh:=53 +2585: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2588: right3 499698 h:=1061786+499698=1561484, hh:=99 +2592: set2 65288("FF08) type=6 h:=1561484+330310=1891794, hh:=120 +2595: set2 12354("3042) type=0 h:=1891794+630598=2522392, hh:=160 +[あ ・ (あ] +2598: pop +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +2599: y0 786432 v:=12451840+786432=13238272, vv:=839 +2600: push +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +2601: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2604: w3 199410 h:=630598+199410=830008, hh:=53 +2608: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2611: w0 199410 h:=1061786+199410=1261196, hh:=80 +2612: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2615: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ ・ (あ] +2618: pop +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +2619: y0 786432 v:=13238272+786432=14024704, vv:=888 +2620: push +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +2621: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2624: right3 199410 h:=630598+199410=830008, hh:=53 +2628: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2631: set2 65288("FF08) type=6 h:=1061786+330310=1392096, hh:=89 +2634: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=129 +[あ ・(あ] +2637: pop +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +2638: y0 786432 v:=14024704+786432=14811136, vv:=938 +2639: push +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +2640: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2643: w3 199410 h:=630598+199410=830008, hh:=53 +2647: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2650: w0 199410 h:=1061786+199410=1261196, hh:=80 +2651: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2654: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ ・ (あ] +2657: pop +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +2658: y0 786432 v:=14811136+786432=15597568, vv:=988 +2659: push +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +2660: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2663: w3 199410 h:=630598+199410=830008, hh:=53 +2667: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2670: w0 199410 h:=1061786+199410=1261196, hh:=80 +2671: set2 65288("FF08) type=6 h:=1261196+330310=1591506, hh:=101 +2674: set2 12354("3042) type=0 h:=1591506+630598=2222104, hh:=141 +[あ ・ (あ] +2677: pop +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +2678: y0 786432 v:=15597568+786432=16384000, vv:=1038 +2679: push +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +2680: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2683: right3 199410 h:=630598+199410=830008, hh:=53 +2687: set2 12539("30FB) type=4 h:=830008+231778=1061786, hh:=68 +2690: set2 65288("FF08) type=6 h:=1061786+330310=1392096, hh:=89 +2693: set2 12354("3042) type=0 h:=1392096+630598=2022694, hh:=129 +[あ ・(あ] +2696: pop +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +2697: y0 786432 v:=16384000+786432=17170432, vv:=1088 +2698: push +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +2699: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +2702: fntnum1 current font is cmtt10 +2703: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +2704: setchar105 ("69) h:=974659+344061=1318720, hh:=84 +2705: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106 +2706: setchar104 ("68) h:=1662781+344061=2006842, hh:=128 +2707: setchar105 ("69) h:=2006842+344061=2350903, hh:=150 +2708: setchar98 ("62) h:=2350903+344061=2694964, hh:=172 +2709: setchar105 ("69) h:=2694964+344061=3039025, hh:=194 +2710: setchar116 ("74) h:=3039025+344061=3383086, hh:=216 +2711: setchar103 ("67) h:=3383086+344061=3727147, hh:=238 +2712: setchar108 ("6C) h:=3727147+344061=4071208, hh:=260 +2713: setchar117 ("75) h:=4071208+344061=4415269, hh:=282 +2714: setchar101 ("65) h:=4415269+344061=4759330, hh:=303 +2715: w3 344061 h:=4759330+344061=5103391, hh:=323 +2719: setchar97 ("61) h:=5103391+344061=5447452, hh:=345 +2720: setchar116 ("74) h:=5447452+344061=5791513, hh:=367 +2721: w0 344061 h:=5791513+344061=6135574, hh:=389 +[\inhibitglue at ] +2722: fntnum2 current font is umin10 +2723: set2 20869("5185) type=0 h:=6135574+630598=6766172, hh:=429 +2726: set2 20596("5074) type=0 h:=6766172+630598=7396770, hh:=469 +2729: set2 12522("30EA) type=10 h:=7396770+630598=8027368, hh:=509 +2732: set2 12473("30B9) type=0 h:=8027368+630598=8657966, hh:=549 +2735: set2 12488("30C8) type=10 h:=8657966+630598=9288564, hh:=589 +[内側リスト] +2738: pop +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +2739: y0 786432 v:=17170432+786432=17956864, vv:=1137 +2740: push +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +2741: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2744: set2 65288("FF08) type=6 h:=630598+330310=960908, hh:=61 +[あ(] +2747: pop +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +2748: y0 786432 v:=17956864+786432=18743296, vv:=1187 +2749: push +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +2750: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2753: right3 300288 h:=630598+300288=930886, hh:=59 +2757: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80 +[あ (] +2760: pop +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +2761: y0 786432 v:=18743296+786432=19529728, vv:=1237 +2762: push +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +2763: set2 65289("FF09) type=1 h:=0+330310=330310, hh:=21 +2766: right3 600576 h:=330310+600576=930886, hh:=59 +2770: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80 +[) (] +2773: pop +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +2774: y0 786432 v:=19529728+786432=20316160, vv:=1287 +2775: push +level 1:(h=0,v=20316160,w=0,x=0,y=786432,z=0,hh=0,vv=1287) +2776: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2779: w3 300288 h:=630598+300288=930886, hh:=59 +2783: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80 +2786: set2 12356("3044) type=0 h:=1261196+630598=1891794, hh:=120 +[あ (い] +2789: push +level 2:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287) +2790: push +level 3:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287) +2791: w0 300288 h:=1891794+300288=2192082, hh:=139 +2792: set2 12300("300C) type=6 h:=2192082+330310=2522392, hh:=160 +[ 「] +2795: pop +level 3:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287) +2796: pop +level 2:(h=1891794,v=20316160,w=300288,x=0,y=786432,z=0,hh=120,vv=1287) +2797: pop +level 1:(h=0,v=20316160,w=0,x=0,y=786432,z=0,hh=0,vv=1287) +2798: y0 786432 v:=20316160+786432=21102592, vv:=1337 +2799: push +level 1:(h=0,v=21102592,w=0,x=0,y=786432,z=0,hh=0,vv=1337) +2800: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2803: w3 300288 h:=630598+300288=930886, hh:=59 +2807: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80 +2810: set2 12356("3044) type=0 h:=1261196+630598=1891794, hh:=120 +[あ (い] +2813: push +level 2:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337) +2814: push +level 3:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337) +2815: w0 300288 h:=1891794+300288=2192082, hh:=139 +2816: set2 12300("300C) type=6 h:=2192082+330310=2522392, hh:=160 +[ 「] +2819: pop +level 3:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337) +2820: pop +level 2:(h=1891794,v=21102592,w=300288,x=0,y=786432,z=0,hh=120,vv=1337) +2821: pop +level 1:(h=0,v=21102592,w=0,x=0,y=786432,z=0,hh=0,vv=1337) +2822: y0 786432 v:=21102592+786432=21889024, vv:=1386 +2823: push +level 1:(h=0,v=21889024,w=0,x=0,y=786432,z=0,hh=0,vv=1386) +2824: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2827: set2 65288("FF08) type=6 h:=630598+330310=960908, hh:=61 +2830: set2 12356("3044) type=0 h:=960908+630598=1591506, hh:=101 +[あ(い] +2833: push +level 2:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386) +2834: push +level 3:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386) +2835: right3 300288 h:=1591506+300288=1891794, hh:=120 +2839: set2 12300("300C) type=6 h:=1891794+330310=2222104, hh:=141 +[ 「] +2842: pop +level 3:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386) +2843: pop +level 2:(h=1591506,v=21889024,w=0,x=0,y=786432,z=0,hh=101,vv=1386) +2844: pop +level 1:(h=0,v=21889024,w=0,x=0,y=786432,z=0,hh=0,vv=1386) +2845: y0 786432 v:=21889024+786432=22675456, vv:=1436 +2846: push +level 1:(h=0,v=22675456,w=0,x=0,y=786432,z=0,hh=0,vv=1436) +2847: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2850: right3 300288 h:=630598+300288=930886, hh:=59 +2854: set2 65288("FF08) type=6 h:=930886+330310=1261196, hh:=80 +2857: set2 12356("3044) type=0 h:=1261196+630598=1891794, hh:=120 +[あ (い] +2860: push +level 2:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436) +2861: push +level 3:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436) +2862: set2 12300("300C) type=6 h:=1891794+330310=2222104, hh:=141 +[「] +2865: pop +level 3:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436) +2866: pop +level 2:(h=1891794,v=22675456,w=0,x=0,y=786432,z=0,hh=120,vv=1436) +2867: pop +level 1:(h=0,v=22675456,w=0,x=0,y=786432,z=0,hh=0,vv=1436) +2868: y0 786432 v:=22675456+786432=23461888, vv:=1486 +2869: push +level 1:(h=0,v=23461888,w=0,x=0,y=786432,z=0,hh=0,vv=1486) +2870: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +2873: fntnum1 current font is cmtt10 +2874: setchar92 ("5C) h:=630598+344061=974659, hh:=62 +2875: setchar105 ("69) h:=974659+344061=1318720, hh:=84 +2876: setchar110 ("6E) h:=1318720+344061=1662781, hh:=106 +2877: setchar104 ("68) h:=1662781+344061=2006842, hh:=128 +2878: setchar105 ("69) h:=2006842+344061=2350903, hh:=150 +2879: setchar98 ("62) h:=2350903+344061=2694964, hh:=172 +2880: setchar105 ("69) h:=2694964+344061=3039025, hh:=194 +2881: setchar116 ("74) h:=3039025+344061=3383086, hh:=216 +2882: setchar103 ("67) h:=3383086+344061=3727147, hh:=238 +2883: setchar108 ("6C) h:=3727147+344061=4071208, hh:=260 +2884: setchar117 ("75) h:=4071208+344061=4415269, hh:=282 +2885: setchar101 ("65) h:=4415269+344061=4759330, hh:=303 +2886: right3 344061 h:=4759330+344061=5103391, hh:=323 +2890: setchar116 ("74) h:=5103391+344061=5447452, hh:=345 +2891: setchar101 ("65) h:=5447452+344061=5791513, hh:=367 +2892: setchar115 ("73) h:=5791513+344061=6135574, hh:=389 +2893: setchar116 ("74) h:=6135574+344061=6479635, hh:=411 +[\inhibitglue test] +2894: pop +level 1:(h=0,v=23461888,w=0,x=0,y=786432,z=0,hh=0,vv=1486) +2895: y0 786432 v:=23461888+786432=24248320, vv:=1536 +2896: push +level 1:(h=0,v=24248320,w=0,x=0,y=786432,z=0,hh=0,vv=1536) +2897: fntnum2 current font is umin10 +2898: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2901: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2904: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101 +[あ)あ] +2907: pop +level 1:(h=0,v=24248320,w=0,x=0,y=786432,z=0,hh=0,vv=1536) +2908: y0 786432 v:=24248320+786432=25034752, vv:=1586 +2909: push +level 1:(h=0,v=25034752,w=0,x=0,y=786432,z=0,hh=0,vv=1586) +2910: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2913: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2916: right3 300288 h:=960908+300288=1261196, hh:=80 +2920: set2 12354("3042) type=0 h:=1261196+630598=1891794, hh:=120 +[あ) あ] +2923: pop +level 1:(h=0,v=25034752,w=0,x=0,y=786432,z=0,hh=0,vv=1586) +2924: y0 786432 v:=25034752+786432=25821184, vv:=1636 +2925: push +level 1:(h=0,v=25821184,w=0,x=0,y=786432,z=0,hh=0,vv=1636) +2926: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2929: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2932: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101 +[あ)あ] +2935: pop +level 1:(h=0,v=25821184,w=0,x=0,y=786432,z=0,hh=0,vv=1636) +2936: y0 786432 v:=25821184+786432=26607616, vv:=1685 +2937: push +level 1:(h=0,v=26607616,w=0,x=0,y=786432,z=0,hh=0,vv=1685) +2938: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2941: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2944: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101 +[あ)あ] +2947: pop +level 1:(h=0,v=26607616,w=0,x=0,y=786432,z=0,hh=0,vv=1685) +2948: y0 786432 v:=26607616+786432=27394048, vv:=1735 +2949: push +level 1:(h=0,v=27394048,w=0,x=0,y=786432,z=0,hh=0,vv=1735) +2950: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2953: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2956: right3 300288 h:=960908+300288=1261196, hh:=80 +2960: set2 12354("3042) type=0 h:=1261196+630598=1891794, hh:=120 +[あ) あ] +2963: pop +level 1:(h=0,v=27394048,w=0,x=0,y=786432,z=0,hh=0,vv=1735) +2964: y0 786432 v:=27394048+786432=28180480, vv:=1785 +2965: push +level 1:(h=0,v=28180480,w=0,x=0,y=786432,z=0,hh=0,vv=1785) +2966: set2 12354("3042) type=0 h:=0+630598=630598, hh:=40 +2969: set2 65289("FF09) type=1 h:=630598+330310=960908, hh:=61 +2972: set2 12354("3042) type=0 h:=960908+630598=1591506, hh:=101 +[あ)あ] +2975: pop +level 1:(h=0,v=28180480,w=0,x=0,y=786432,z=0,hh=0,vv=1785) +2976: y0 786432 v:=28180480+786432=28966912, vv:=1835 +2977: push +level 1:(h=0,v=28966912,w=0,x=0,y=786432,z=0,hh=0,vv=1835) +2978: down3 91000 v:=28966912+91000=29057912, vv:=1841 +2982: setrule height 600600, width 26214 (39x2 pixels) + h:=0+26214=26214, hh:=2 +2991: down3 -91000 v:=29057912-91000=28966912, vv:=1835 +2995: set2 65307("FF1B) type=4 h:=26214+231778=257992, hh:=17 +2998: right3 199410 h:=257992+199410=457402, hh:=29 +3002: set2 12354("3042) type=0 h:=457402+630598=1088000, hh:=69 +3005: right3 300288 h:=1088000+300288=1388288, hh:=88 +3009: set2 65288("FF08) type=6 h:=1388288+330310=1718598, hh:=109 +[; あ (] +3012: pop +level 1:(h=0,v=28966912,w=0,x=0,y=786432,z=0,hh=0,vv=1835) +3013: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +3014: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +3018: eop diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ b/Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ new file mode 100644 index 00000000000..27132f1cea0 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/eup-jctrlsyma.typ @@ -0,0 +1,121 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 287. +maxv=43725786, maxh=30785863, maxstackdepth=2, totalpages=1 +Font 1: umin10 (JFM)---loaded at size 655360 DVI units +Font 0: cmr10---loaded at size 655360 DVI units + +42: beginning of page 1 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: cmr10 +130: fntnum0 current font is cmr10 +131: setchar49 ("31) h:=1310720+327681=1638401, hh:=104 +132: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +133: right3 291271 h:=1820446+291271=2111717, hh:=134 +[1. ] +137: fntdef1 1: umin10 +159: fntnum1 current font is umin10 +160: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174 +163: set2 9671("25C7) type=0 h:=2742315+630598=3372913, hh:=214 +166: set2 9670("25C6) type=0 h:=3372913+630598=4003511, hh:=254 +169: set2 9671("25C7) type=0 h:=4003511+630598=4634109, hh:=294 +[◆◇◆◇] +172: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +173: y3 786432 v:=655360+786432=1441792, vv:=91 +177: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +178: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +182: fntnum0 current font is cmr10 +183: setchar50 ("32) h:=1310720+327681=1638401, hh:=104 +184: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +185: right3 291271 h:=1820446+291271=2111717, hh:=134 +[2. ] +189: fntnum1 current font is umin10 +190: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174 +193: set2 9671("25C7) type=0 h:=2742315+630598=3372913, hh:=214 +[◆◇] +196: fntnum0 current font is cmr10 +197: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226 +198: right3 218453 h:=3554958+218453=3773411, hh:=239 +[, ] +202: fntnum1 current font is umin10 +203: set2 9670("25C6) type=0 h:=3773411+630598=4404009, hh:=279 +206: set2 9671("25C7) type=0 h:=4404009+630598=5034607, hh:=319 +[◆◇] +209: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +210: y0 786432 v:=1441792+786432=2228224, vv:=141 +211: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +212: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +216: fntnum0 current font is cmr10 +217: setchar51 ("33) h:=1310720+327681=1638401, hh:=104 +218: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +219: right3 291271 h:=1820446+291271=2111717, hh:=134 +[3. ] +223: fntnum1 current font is umin10 +224: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174 +227: set2 9671("25C7) type=0 h:=2742315+630598=3372913, hh:=214 +[◆◇] +230: fntnum0 current font is cmr10 +231: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226 +232: w3 218453 h:=3554958+218453=3773411, hh:=239 +[, ] +236: fntnum1 current font is umin10 +237: set2 9670("25C6) type=0 h:=3773411+630598=4404009, hh:=279 +240: w0 218453 h:=4404009+218453=4622462, hh:=293 +241: set2 9671("25C7) type=0 h:=4622462+630598=5253060, hh:=333 +[◆ ◇] +244: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +245: y0 786432 v:=2228224+786432=3014656, vv:=191 +246: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +247: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +251: fntnum0 current font is cmr10 +252: setchar52 ("34) h:=1310720+327681=1638401, hh:=104 +253: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +254: right3 291271 h:=1820446+291271=2111717, hh:=134 +[4. ] +258: fntnum1 current font is umin10 +259: set2 9670("25C6) type=0 h:=2111717+630598=2742315, hh:=174 +262: w3 218453 h:=2742315+218453=2960768, hh:=188 +266: set2 9671("25C7) type=0 h:=2960768+630598=3591366, hh:=228 +[◆ ◇] +269: fntnum0 current font is cmr10 +270: setchar44 ("2C) h:=3591366+182045=3773411, hh:=240 +271: w0 218453 h:=3773411+218453=3991864, hh:=253 +[, ] +272: fntnum1 current font is umin10 +273: set2 9670("25C6) type=0 h:=3991864+630598=4622462, hh:=293 +276: w0 218453 h:=4622462+218453=4840915, hh:=307 +277: set2 9671("25C7) type=0 h:=4840915+630598=5471513, hh:=347 +[◆ ◇] +280: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +281: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +282: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +286: eop diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ new file mode 100644 index 00000000000..cf8d08f7715 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ea.typ @@ -0,0 +1,825 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 1055. +maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1 +Font 1: min10 (JFM)---loaded at size 655360 DVI units +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 50623 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: ec-lmr10 +133: fntnum0 current font is ec-lmr10 +134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114 +135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126 +136: w3 218453 h:=1984283+218453=2202736, hh:=140 +[A: ] +140: set1 197("C5) h:=2202736+491520=2694256, hh:=171 +142: set1 191("BF) h:=2694256+491520=3185776, hh:=202 +144: w0 218453 h:=3185776+218453=3404229, hh:=216 +[ ] +145: set1 197("C5) h:=3404229+491520=3895749, hh:=247 +147: set1 191("BF) h:=3895749+491520=4387269, hh:=278 +149: w0 218453 h:=4387269+218453=4605722, hh:=292 +[ ] +150: fntdef1 1: min10 +171: fntnum1 current font is min10 +172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332 +[䔿] +175: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +176: y3 786432 v:=655360+786432=1441792, vv:=91 +180: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +181: push +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +185: pop +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +186: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +190: fntnum0 current font is ec-lmr10 +191: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96 +193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114 +194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135 +195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158 +196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170 +197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193 +198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214 +199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226 +[\meaning.] +200: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +201: y0 786432 v:=1441792+786432=2228224, vv:=141 +202: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +203: right3 1310720 h:=0+1310720=1310720, hh:=83 +207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287 +216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308 +217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326 +218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342 +219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363 +220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375 +221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389 +222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421 +[ macro:->macro:->] +223: set1 197("C5) h:=6630046+491520=7121566, hh:=452 +225: set1 191("BF) h:=7121566+491520=7613086, hh:=483 +227: w3 218453 h:=7613086+218453=7831539, hh:=496 +[ ] +231: set1 197("C5) h:=7831539+491520=8323059, hh:=527 +233: set1 191("BF) h:=8323059+491520=8814579, hh:=558 +235: w0 218453 h:=8814579+218453=9033032, hh:=572 +[ ] +236: fntnum1 current font is min10 +237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612 +[䔿] +240: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +241: y0 786432 v:=2228224+786432=3014656, vv:=191 +242: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +243: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +247: fntnum0 current font is ec-lmr10 +248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +[macro:->] +256: set1 197("C5) h:=3970383+491520=4461903, hh:=283 +258: set1 191("BF) h:=4461903+491520=4953423, hh:=314 +260: w3 218453 h:=4953423+218453=5171876, hh:=328 +[ ] +264: set1 197("C5) h:=5171876+491520=5663396, hh:=359 +266: set1 191("BF) h:=5663396+491520=6154916, hh:=390 +268: w0 218453 h:=6154916+218453=6373369, hh:=404 +[ ] +269: fntnum1 current font is min10 +270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444 +[䔿] +273: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +274: y0 786432 v:=3014656+786432=3801088, vv:=241 +275: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +276: push +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +280: pop +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +281: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +285: fntnum0 current font is ec-lmr10 +286: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74 +288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95 +289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118 +290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141 +291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162 +292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197 +293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215 +294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227 +[\jobname.] +295: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +296: y0 786432 v:=3801088+786432=4587520, vv:=291 +297: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +298: push +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +299: fntnum1 current font is min10 +300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +303: pop +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +304: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +308: fntnum0 current font is ec-lmr10 +309: setchar111 ("6F) h:=630598+327680=958278, hh:=61 +310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84 +311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102 +312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114 +313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132 +314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148 +315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164 +316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182 +317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198 +318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210 +[oneletter.] +319: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +320: y0 786432 v:=4587520+786432=5373952, vv:=340 +321: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +322: right3 1310720 h:=0+1310720=1310720, hh:=83 +326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +327: set1 196("C4) h:=1638400+491520=2129920, hh:=135 +329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147 +330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168 +[.\] +331: set1 197("C5) h:=2639643+491520=3131163, hh:=199 +333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211 +334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232 +[.\] +335: set1 255("FF) h:=3640886+327680=3968566, hh:=253 +337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265 +338: right3 291271 h:=4150609+291271=4441880, hh:=281 +[. ] +342: set1 191("BF) h:=4441880+491520=4933400, hh:=312 +344: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +345: y0 786432 v:=5373952+786432=6160384, vv:=390 +346: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +347: push +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +348: fntnum1 current font is min10 +349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +352: pop +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +353: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +357: fntnum0 current font is ec-lmr10 +358: setchar99 ("63) h:=630598+291275=921873, hh:=58 +359: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192 +365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname1.] +366: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +367: y0 786432 v:=6160384+786432=6946816, vv:=440 +368: push +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +369: right3 1310720 h:=0+1310720=1310720, hh:=83 +373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +374: set1 197("C5) h:=1638400+491520=2129920, hh:=135 +376: set1 191("BF) h:=2129920+491520=2621440, hh:=166 +378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187 +[\] +379: set1 197("C5) h:=2949120+491520=3440640, hh:=218 +381: set1 191("BF) h:=3440640+491520=3932160, hh:=249 +383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261 +384: w3 218453 h:=4114203+218453=4332656, hh:=274 +388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295 +[, \] +389: set1 197("C5) h:=4660336+491520=5151856, hh:=326 +391: set1 191("BF) h:=5151856+491520=5643376, hh:=357 +393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369 +394: w0 218453 h:=5825419+218453=6043872, hh:=383 +395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404 +[, \] +396: fntnum1 current font is min10 +397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444 +[䔿] +400: pop +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +401: y0 786432 v:=6946816+786432=7733248, vv:=490 +402: push +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +403: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +407: fntnum0 current font is ec-lmr10 +408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[\] +409: fntnum1 current font is min10 +410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144 +[␢] +413: fntnum0 current font is ec-lmr10 +414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176 +415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208 +416: right3 218453 h:=3288474+218453=3506927, hh:=222 +420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245 +421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266 +422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287 +423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305 +424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317 +[=> hoge,] +425: pop +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +426: y0 786432 v:=7733248+786432=8519680, vv:=540 +427: push +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +428: right3 1310720 h:=0+1310720=1310720, hh:=83 +432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +433: set1 164("A4) h:=1638400+364085=2002485, hh:=127 +435: set1 162("A2) h:=2002485+291275=2293760, hh:=145 +437: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177 +438: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209 +439: right3 218453 h:=3313236+218453=3531689, hh:=224 +443: setchar112 ("70) h:=3531689+364085=3895774, hh:=247 +444: setchar105 ("69) h:=3895774+182043=4077817, hh:=259 +445: setchar121 ("79) h:=4077817+345898=4423715, hh:=281 +446: right2 -18205 h:=4423715-18205=4405510, hh:=280 +449: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301 +450: setchar44 ("2C) h:=4733190+182043=4915233, hh:=313 +[=> piyo,] +451: pop +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +452: y0 786432 v:=8519680+786432=9306112, vv:=589 +453: push +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +454: right3 1310720 h:=0+1310720=1310720, hh:=83 +458: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +459: set1 164("A4) h:=1638400+364085=2002485, hh:=127 +461: set1 162("A2) h:=2002485+291275=2293760, hh:=145 +463: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177 +464: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209 +465: right3 218453 h:=3313236+218453=3531689, hh:=224 +469: setchar112 ("70) h:=3531689+364085=3895774, hh:=247 +470: setchar105 ("69) h:=3895774+182043=4077817, hh:=259 +471: setchar121 ("79) h:=4077817+345898=4423715, hh:=281 +472: right2 -18205 h:=4423715-18205=4405510, hh:=280 +475: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301 +476: setchar46 ("2E) h:=4733190+182043=4915233, hh:=313 +[=> piyo.] +477: pop +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +478: y0 786432 v:=9306112+786432=10092544, vv:=639 +479: push +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +480: push +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +481: fntnum1 current font is min10 +482: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +485: pop +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +486: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +490: fntnum0 current font is ec-lmr10 +491: setchar48 ("30) h:=630598+327680=958278, hh:=61 +492: setchar120 ("78) h:=958278+345898=1304176, hh:=83 +493: setchar70 ("46) h:=1304176+427818=1731994, hh:=110 +494: setchar70 ("46) h:=1731994+427818=2159812, hh:=137 +495: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149 +[0xFF.] +496: pop +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +497: y0 786432 v:=10092544+786432=10878976, vv:=689 +498: push +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +499: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +503: fntnum1 current font is min10 +504: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +507: fntnum0 current font is ec-lmr10 +508: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +509: w3 218453 h:=2123361+218453=2341814, hh:=148 +513: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +514: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +515: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +517: set1 197("C5) h:=3252044+491520=3743564, hh:=237 +519: set1 255("FF) h:=3743564+327680=4071244, hh:=258 +521: set1 191("BF) h:=4071244+491520=4562764, hh:=289 +523: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301 +524: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336 +525: setchar97 ("61) h:=5290918+327680=5618598, hh:=357 +526: setchar99 ("63) h:=5618598+291275=5909873, hh:=375 +527: setchar114 ("72) h:=5909873+256683=6166556, hh:=391 +528: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412 +529: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424 +530: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438 +531: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470 +[:macro:->] +532: fntnum1 current font is min10 +533: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510 +[␢] +536: fntnum0 current font is ec-lmr10 +537: setchar41 ("29) h:=8035068+254870=8289938, hh:=526 +538: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538 +539: w0 218453 h:=8471981+218453=8690434, hh:=550 +540: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571 +[), \] +541: set1 255("FF) h:=9018114+327680=9345794, hh:=592 +543: set1 197("C5) h:=9345794+491520=9837314, hh:=623 +545: set1 255("FF) h:=9837314+327680=10164994, hh:=644 +547: set1 191("BF) h:=10164994+491520=10656514, hh:=675 +549: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687 +550: w0 218453 h:=10838557+218453=11057010, hh:=700 +551: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735 +552: setchar97 ("61) h:=11603121+327680=11930801, hh:=756 +553: setchar99 ("63) h:=11930801+291275=12222076, hh:=774 +554: setchar114 ("72) h:=12222076+256683=12478759, hh:=790 +555: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811 +556: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823 +557: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837 +558: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869 +559: setchar40 ("28) h:=13716673+254870=13971543, hh:=885 +560: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906 +[, macro:->(\] +561: set1 255("FF) h:=14299223+327680=14626903, hh:=927 +563: set1 197("C5) h:=14626903+491520=15118423, hh:=958 +565: set1 255("FF) h:=15118423+327680=15446103, hh:=979 +567: set1 191("BF) h:=15446103+491520=15937623, hh:=1010 +569: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022 +570: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057 +571: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078 +572: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096 +573: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112 +574: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133 +575: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145 +576: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159 +577: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191 +[:macro:->] +578: fntnum1 current font is min10 +579: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231 +[␢] +582: fntnum0 current font is ec-lmr10 +583: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247 +584: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259 +[).] +585: pop +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +586: y0 786432 v:=10878976+786432=11665408, vv:=739 +587: push +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +588: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +592: fntnum1 current font is min10 +593: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +596: fntnum0 current font is ec-lmr10 +597: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +598: w3 218453 h:=2123361+218453=2341814, hh:=148 +602: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +603: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +604: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +606: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218 +607: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253 +608: setchar97 ("61) h:=3980198+327680=4307878, hh:=274 +609: setchar99 ("63) h:=4307878+291275=4599153, hh:=292 +610: setchar114 ("72) h:=4599153+256683=4855836, hh:=308 +611: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329 +612: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341 +613: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355 +614: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387 +[:macro:->] +615: fntnum1 current font is min10 +616: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427 +[] +619: fntnum0 current font is ec-lmr10 +620: setchar41 ("29) h:=6724348+254870=6979218, hh:=443 +621: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455 +622: w0 218453 h:=7161261+218453=7379714, hh:=467 +623: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488 +[), \] +624: set1 255("FF) h:=7707394+327680=8035074, hh:=509 +626: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521 +627: w0 218453 h:=8217117+218453=8435570, hh:=534 +628: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569 +629: setchar97 ("61) h:=8981681+327680=9309361, hh:=590 +630: setchar99 ("63) h:=9309361+291275=9600636, hh:=608 +631: setchar114 ("72) h:=9600636+256683=9857319, hh:=624 +632: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645 +633: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657 +634: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671 +635: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703 +636: setchar40 ("28) h:=11095233+254870=11350103, hh:=719 +637: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740 +[, macro:->(\] +638: set1 255("FF) h:=11677783+327680=12005463, hh:=761 +640: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773 +641: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808 +642: setchar97 ("61) h:=12733617+327680=13061297, hh:=829 +643: setchar99 ("63) h:=13061297+291275=13352572, hh:=847 +644: setchar114 ("72) h:=13352572+256683=13609255, hh:=863 +645: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884 +646: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896 +647: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910 +648: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942 +[:macro:->] +649: fntnum1 current font is min10 +650: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982 +[] +653: fntnum0 current font is ec-lmr10 +654: setchar41 ("29) h:=15477767+254870=15732637, hh:=998 +655: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010 +[).] +656: pop +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +657: y0 786432 v:=11665408+786432=12451840, vv:=789 +658: push +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +659: push +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +660: fntnum1 current font is min10 +661: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +664: pop +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +665: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +669: fntnum0 current font is ec-lmr10 +670: setchar99 ("63) h:=630598+291275=921873, hh:=58 +671: setchar111 ("6F) h:=921873+327680=1249553, hh:=79 +672: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102 +673: right2 -18205 h:=1613638-18205=1595433, hh:=101 +676: setchar116 ("74) h:=1595433+254870=1850303, hh:=117 +677: setchar101 ("65) h:=1850303+291275=2141578, hh:=135 +678: setchar120 ("78) h:=2141578+345898=2487476, hh:=157 +679: setchar116 ("74) h:=2487476+254870=2742346, hh:=173 +680: setchar115 ("73) h:=2742346+258506=3000852, hh:=189 +681: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201 +[contexts.] +682: pop +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +683: y0 786432 v:=12451840+786432=13238272, vv:=839 +684: push +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +685: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +689: fntnum1 current font is min10 +690: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +693: fntnum0 current font is ec-lmr10 +694: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +696: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +698: w3 218453 h:=2924358+218453=3142811, hh:=199 +702: setchar97 ("61) h:=3142811+327680=3470491, hh:=220 +[ a] +703: set1 197("C5) h:=3470491+491520=3962011, hh:=251 +705: set1 191("BF) h:=3962011+491520=4453531, hh:=282 +707: w0 218453 h:=4453531+218453=4671984, hh:=296 +[ ] +708: set1 197("C5) h:=4671984+491520=5163504, hh:=327 +710: set1 191("BF) h:=5163504+491520=5655024, hh:=358 +712: w0 218453 h:=5655024+218453=5873477, hh:=372 +[ ] +713: fntnum1 current font is min10 +714: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412 +717: w0 218453 h:=6504075+218453=6722528, hh:=426 +[䔿 ] +718: fntnum0 current font is ec-lmr10 +719: set1 197("C5) h:=6722528+491520=7214048, hh:=457 +721: set1 191("BF) h:=7214048+491520=7705568, hh:=488 +723: w0 218453 h:=7705568+218453=7924021, hh:=502 +[ ] +724: set1 197("C5) h:=7924021+491520=8415541, hh:=533 +726: set1 191("BF) h:=8415541+491520=8907061, hh:=564 +728: w0 218453 h:=8907061+218453=9125514, hh:=578 +[ ] +729: fntnum1 current font is min10 +730: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618 +733: x3 436906 h:=9756112+436906=10193018, hh:=646 +[䔿 ] +737: fntnum0 current font is ec-lmr10 +738: set1 197("C5) h:=10193018+491520=10684538, hh:=677 +740: set1 191("BF) h:=10684538+491520=11176058, hh:=708 +742: w0 218453 h:=11176058+218453=11394511, hh:=722 +[ ] +743: fntnum1 current font is min10 +744: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762 +747: x0 436906 h:=12025109+436906=12462015, hh:=789 +748: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829 +[䔿 ] +751: fntnum0 current font is ec-lmr10 +752: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864 +753: setchar97 ("61) h:=13638724+327680=13966404, hh:=885 +754: setchar99 ("63) h:=13966404+291275=14257679, hh:=903 +755: setchar114 ("72) h:=14257679+256683=14514362, hh:=919 +756: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940 +757: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952 +758: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966 +759: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998 +[macro:->] +760: set1 197("C5) h:=15752276+491520=16243796, hh:=1029 +762: set1 191("BF) h:=16243796+491520=16735316, hh:=1060 +764: w0 218453 h:=16735316+218453=16953769, hh:=1074 +[ ] +765: set1 197("C5) h:=16953769+491520=17445289, hh:=1105 +767: set1 191("BF) h:=17445289+491520=17936809, hh:=1136 +769: w0 218453 h:=17936809+218453=18155262, hh:=1150 +[ ] +770: fntnum1 current font is min10 +771: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190 +[䔿] +774: pop +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +775: y0 786432 v:=13238272+786432=14024704, vv:=888 +776: push +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +777: right3 1310720 h:=0+1310720=1310720, hh:=83 +781: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[ ␢] +784: fntnum0 current font is ec-lmr10 +785: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +787: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +789: w3 218453 h:=2924358+218453=3142811, hh:=199 +[ ] +793: set1 197("C5) h:=3142811+491520=3634331, hh:=230 +795: set1 191("BF) h:=3634331+491520=4125851, hh:=261 +797: fntnum1 current font is min10 +798: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301 +[䔿] +801: fntnum0 current font is ec-lmr10 +802: set1 197("C5) h:=4756449+491520=5247969, hh:=332 +804: set1 191("BF) h:=5247969+491520=5739489, hh:=363 +806: w0 218453 h:=5739489+218453=5957942, hh:=377 +807: setchar97 ("61) h:=5957942+327680=6285622, hh:=398 +[ a] +808: set1 197("C5) h:=6285622+491520=6777142, hh:=429 +810: set1 191("BF) h:=6777142+491520=7268662, hh:=460 +812: w0 218453 h:=7268662+218453=7487115, hh:=474 +[ ] +813: set1 197("C5) h:=7487115+491520=7978635, hh:=505 +815: set1 191("BF) h:=7978635+491520=8470155, hh:=536 +817: w0 218453 h:=8470155+218453=8688608, hh:=550 +[ ] +818: fntnum1 current font is min10 +819: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590 +822: w0 218453 h:=9319206+218453=9537659, hh:=604 +[䔿 ] +823: fntnum0 current font is ec-lmr10 +824: set1 197("C5) h:=9537659+491520=10029179, hh:=635 +826: set1 191("BF) h:=10029179+491520=10520699, hh:=666 +828: w0 218453 h:=10520699+218453=10739152, hh:=680 +[ ] +829: set1 197("C5) h:=10739152+491520=11230672, hh:=711 +831: set1 191("BF) h:=11230672+491520=11722192, hh:=742 +833: w0 218453 h:=11722192+218453=11940645, hh:=756 +[ ] +834: fntnum1 current font is min10 +835: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796 +838: right3 436906 h:=12571243+436906=13008149, hh:=824 +[䔿 ] +842: fntnum0 current font is ec-lmr10 +843: set1 197("C5) h:=13008149+491520=13499669, hh:=855 +845: set1 191("BF) h:=13499669+491520=13991189, hh:=886 +847: w0 218453 h:=13991189+218453=14209642, hh:=900 +[ ] +848: fntnum1 current font is min10 +849: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940 +852: w0 218453 h:=14840240+218453=15058693, hh:=954 +853: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994 +[䔿 ] +856: fntnum0 current font is ec-lmr10 +857: set1 255("FF) h:=15689291+327680=16016971, hh:=1015 +859: pop +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +860: y0 786432 v:=14024704+786432=14811136, vv:=938 +861: push +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +862: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +866: fntnum1 current font is min10 +867: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +870: pop +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +871: y0 786432 v:=14811136+786432=15597568, vv:=988 +872: push +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +873: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +877: fntnum0 current font is ec-lmr10 +878: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +879: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +880: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +881: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +882: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +883: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +884: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +885: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +886: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +887: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[.a...c.b..] +888: pop +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +889: y0 786432 v:=15597568+786432=16384000, vv:=1038 +890: push +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +891: right3 1310720 h:=0+1310720=1310720, hh:=83 +895: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +896: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +897: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +898: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +899: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +900: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +901: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +902: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +903: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +904: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[ .a...c.b..] +905: pop +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +906: y0 786432 v:=16384000+786432=17170432, vv:=1088 +907: push +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +908: push +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +909: fntnum1 current font is min10 +910: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +913: pop +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +914: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +918: fntnum0 current font is ec-lmr10 +919: setchar99 ("63) h:=630598+291275=921873, hh:=58 +920: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +921: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +922: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +923: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +924: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +925: setchar50 ("32) h:=2709530+327680=3037210, hh:=192 +926: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname2.] +927: pop +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +928: y0 786432 v:=17170432+786432=17956864, vv:=1137 +929: push +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +930: right3 1310720 h:=0+1310720=1310720, hh:=83 +934: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +935: setchar72 ("48) h:=1674805+491520=2166325, hh:=137 +936: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169 +937: right2 -18205 h:=2676063-18205=2657858, hh:=168 +940: setchar87 ("57) h:=2657858+673578=3331436, hh:=211 +941: right3 218453 h:=3331436+218453=3549889, hh:=225 +945: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237 +946: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249 +947: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261 +[ SHOW ...] +948: pop +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +949: y0 786432 v:=17956864+786432=18743296, vv:=1187 +950: push +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +951: right3 1310720 h:=0+1310720=1310720, hh:=83 +955: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +956: setchar84 ("54) h:=1674805+473301=2148106, hh:=136 +957: setchar82 ("52) h:=2148106+482410=2630516, hh:=167 +958: setchar73 ("49) h:=2630516+236650=2867166, hh:=182 +959: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213 +960: setchar71 ("47) h:=3358686+514261=3872947, hh:=246 +961: right3 218453 h:=3872947+218453=4091400, hh:=259 +965: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280 +[ STRING \] +966: set1 197("C5) h:=4419080+491520=4910600, hh:=311 +968: set1 191("BF) h:=4910600+491520=5402120, hh:=342 +970: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354 +971: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375 +[.\] +972: fntnum1 current font is min10 +973: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415 +[䔿] +976: fntnum0 current font is ec-lmr10 +977: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427 +978: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448 +[.\] +979: set1 197("C5) h:=7052164+491520=7543684, hh:=479 +981: set1 191("BF) h:=7543684+491520=8035204, hh:=510 +983: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522 +984: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543 +[.\] +985: fntnum1 current font is min10 +986: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583 +[䔿] +989: pop +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +990: y0 786432 v:=18743296+786432=19529728, vv:=1237 +991: push +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +992: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +996: fntnum0 current font is ec-lmr10 +997: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121 +998: setchar69 ("45) h:=1911488+446005=2357493, hh:=149 +999: setchar65 ("41) h:=2357493+491520=2849013, hh:=180 +1000: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211 +1001: setchar73 ("49) h:=3340533+236650=3577183, hh:=226 +1002: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257 +1003: setchar71 ("47) h:=4068703+514261=4582964, hh:=290 +1004: right3 218453 h:=4582964+218453=4801417, hh:=304 +1008: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339 +1009: setchar97 ("61) h:=5347528+327680=5675208, hh:=360 +1010: setchar99 ("63) h:=5675208+291275=5966483, hh:=378 +1011: setchar114 ("72) h:=5966483+256683=6223166, hh:=394 +1012: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415 +1013: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427 +1014: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441 +1015: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473 +1016: setchar97 ("61) h:=7461080+327680=7788760, hh:=494 +1017: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506 +1018: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541 +1019: setchar97 ("61) h:=8516914+327680=8844594, hh:=562 +1020: setchar99 ("63) h:=8844594+291275=9135869, hh:=580 +1021: setchar114 ("72) h:=9135869+256683=9392552, hh:=596 +1022: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617 +1023: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629 +1024: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643 +1025: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675 +1026: setchar98 ("62) h:=10630466+364085=10994551, hh:=698 +1027: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710 +1028: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745 +1029: setchar97 ("61) h:=11722705+327680=12050385, hh:=765 +1030: setchar99 ("63) h:=12050385+291275=12341660, hh:=783 +1031: setchar114 ("72) h:=12341660+256683=12598343, hh:=799 +1032: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820 +1033: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832 +1034: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846 +1035: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878 +1036: setchar97 ("61) h:=13836257+327680=14163937, hh:=899 +1037: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911 +1038: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945 +1039: setchar97 ("61) h:=14892091+327680=15219771, hh:=966 +1040: setchar99 ("63) h:=15219771+291275=15511046, hh:=984 +1041: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000 +1042: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021 +1043: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033 +1044: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047 +1045: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079 +1046: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102 +1047: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114 +[MEANING macro:->a.macro:->b.macro:->a.macro:->b.] +1048: pop +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +1049: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1050: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +1054: eop diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ new file mode 100644 index 00000000000..9373f0bac64 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-sa.typ @@ -0,0 +1,829 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 1061. +maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1 +Font 1: min10 (JFM)---loaded at size 655360 DVI units +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 37726 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: ec-lmr10 +133: fntnum0 current font is ec-lmr10 +134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114 +135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126 +136: w3 218453 h:=1984283+218453=2202736, hh:=140 +[A: ] +140: set1 197("C5) h:=2202736+491520=2694256, hh:=171 +142: set1 191("BF) h:=2694256+491520=3185776, hh:=202 +144: w0 218453 h:=3185776+218453=3404229, hh:=216 +[ ] +145: set1 197("C5) h:=3404229+491520=3895749, hh:=247 +147: set1 191("BF) h:=3895749+491520=4387269, hh:=278 +149: w0 218453 h:=4387269+218453=4605722, hh:=292 +[ ] +150: fntdef1 1: min10 +171: fntnum1 current font is min10 +172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332 +[䔿] +175: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +176: y3 786432 v:=655360+786432=1441792, vv:=91 +180: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +181: push +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +185: pop +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +186: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +190: fntnum0 current font is ec-lmr10 +191: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96 +193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114 +194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135 +195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158 +196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170 +197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193 +198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214 +199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226 +[\meaning.] +200: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +201: y0 786432 v:=1441792+786432=2228224, vv:=141 +202: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +203: right3 1310720 h:=0+1310720=1310720, hh:=83 +207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287 +216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308 +217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326 +218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342 +219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363 +220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375 +221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389 +222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421 +[ macro:->macro:->] +223: set1 197("C5) h:=6630046+491520=7121566, hh:=452 +225: set1 191("BF) h:=7121566+491520=7613086, hh:=483 +227: w3 218453 h:=7613086+218453=7831539, hh:=496 +[ ] +231: set1 197("C5) h:=7831539+491520=8323059, hh:=527 +233: set1 191("BF) h:=8323059+491520=8814579, hh:=558 +235: w0 218453 h:=8814579+218453=9033032, hh:=572 +[ ] +236: fntnum1 current font is min10 +237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612 +[䔿] +240: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +241: y0 786432 v:=2228224+786432=3014656, vv:=191 +242: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +243: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +247: fntnum0 current font is ec-lmr10 +248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +[macro:->] +256: set1 197("C5) h:=3970383+491520=4461903, hh:=283 +258: set1 191("BF) h:=4461903+491520=4953423, hh:=314 +260: w3 218453 h:=4953423+218453=5171876, hh:=328 +[ ] +264: set1 197("C5) h:=5171876+491520=5663396, hh:=359 +266: set1 191("BF) h:=5663396+491520=6154916, hh:=390 +268: w0 218453 h:=6154916+218453=6373369, hh:=404 +[ ] +269: fntnum1 current font is min10 +270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444 +[䔿] +273: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +274: y0 786432 v:=3014656+786432=3801088, vv:=241 +275: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +276: push +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +280: pop +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +281: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +285: fntnum0 current font is ec-lmr10 +286: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74 +288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95 +289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118 +290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141 +291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162 +292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197 +293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215 +294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227 +[\jobname.] +295: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +296: y0 786432 v:=3801088+786432=4587520, vv:=291 +297: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +298: push +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +299: fntnum1 current font is min10 +300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +303: pop +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +304: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +308: fntnum0 current font is ec-lmr10 +309: setchar111 ("6F) h:=630598+327680=958278, hh:=61 +310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84 +311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102 +312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114 +313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132 +314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148 +315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164 +316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182 +317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198 +318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210 +[oneletter.] +319: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +320: y0 786432 v:=4587520+786432=5373952, vv:=340 +321: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +322: right3 1310720 h:=0+1310720=1310720, hh:=83 +326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +327: set1 196("C4) h:=1638400+491520=2129920, hh:=135 +329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147 +330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168 +[.\] +331: set1 197("C5) h:=2639643+491520=3131163, hh:=199 +333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211 +334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232 +[.\] +335: set1 255("FF) h:=3640886+327680=3968566, hh:=253 +337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265 +338: right3 291271 h:=4150609+291271=4441880, hh:=281 +[. ] +342: set1 191("BF) h:=4441880+491520=4933400, hh:=312 +344: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +345: y0 786432 v:=5373952+786432=6160384, vv:=390 +346: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +347: push +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +348: fntnum1 current font is min10 +349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +352: pop +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +353: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +357: fntnum0 current font is ec-lmr10 +358: setchar99 ("63) h:=630598+291275=921873, hh:=58 +359: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192 +365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname1.] +366: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +367: y0 786432 v:=6160384+786432=6946816, vv:=440 +368: push +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +369: right3 1310720 h:=0+1310720=1310720, hh:=83 +373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +374: set1 197("C5) h:=1638400+491520=2129920, hh:=135 +376: set1 191("BF) h:=2129920+491520=2621440, hh:=166 +378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187 +[\] +379: set1 197("C5) h:=2949120+491520=3440640, hh:=218 +381: set1 191("BF) h:=3440640+491520=3932160, hh:=249 +383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261 +384: w3 218453 h:=4114203+218453=4332656, hh:=274 +388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295 +[, \] +389: set1 197("C5) h:=4660336+491520=5151856, hh:=326 +391: set1 191("BF) h:=5151856+491520=5643376, hh:=357 +393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369 +394: w0 218453 h:=5825419+218453=6043872, hh:=383 +395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404 +[, \] +396: fntnum1 current font is min10 +397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444 +[䔿] +400: pop +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +401: y0 786432 v:=6946816+786432=7733248, vv:=490 +402: push +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +403: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +407: fntnum0 current font is ec-lmr10 +408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[\] +409: fntnum1 current font is min10 +410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144 +[␢] +413: fntnum0 current font is ec-lmr10 +414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176 +415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208 +416: right3 218453 h:=3288474+218453=3506927, hh:=222 +420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245 +421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266 +422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287 +423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305 +424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317 +[=> hoge,] +425: pop +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +426: y0 786432 v:=7733248+786432=8519680, vv:=540 +427: push +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +428: right3 1310720 h:=0+1310720=1310720, hh:=83 +432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +433: set1 227("E3) h:=1638400+327680=1966080, hh:=125 +435: set1 129("81) h:=1966080+491520=2457600, hh:=156 +437: w2 -18205 h:=2457600-18205=2439395, hh:=155 +440: set1 130("82) h:=2439395+473301=2912696, hh:=185 +442: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217 +443: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249 +444: right3 218453 h:=3932172+218453=4150625, hh:=263 +448: setchar112 ("70) h:=4150625+364085=4514710, hh:=286 +449: setchar105 ("69) h:=4514710+182043=4696753, hh:=298 +450: setchar121 ("79) h:=4696753+345898=5042651, hh:=320 +451: w0 -18205 h:=5042651-18205=5024446, hh:=319 +452: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340 +453: setchar44 ("2C) h:=5352126+182043=5534169, hh:=352 +[=> piyo,] +454: pop +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +455: y0 786432 v:=8519680+786432=9306112, vv:=589 +456: push +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +457: right3 1310720 h:=0+1310720=1310720, hh:=83 +461: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +462: set1 227("E3) h:=1638400+327680=1966080, hh:=125 +464: set1 129("81) h:=1966080+491520=2457600, hh:=156 +466: w2 -18205 h:=2457600-18205=2439395, hh:=155 +469: set1 130("82) h:=2439395+473301=2912696, hh:=185 +471: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217 +472: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249 +473: right3 218453 h:=3932172+218453=4150625, hh:=263 +477: setchar112 ("70) h:=4150625+364085=4514710, hh:=286 +478: setchar105 ("69) h:=4514710+182043=4696753, hh:=298 +479: setchar121 ("79) h:=4696753+345898=5042651, hh:=320 +480: w0 -18205 h:=5042651-18205=5024446, hh:=319 +481: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340 +482: setchar46 ("2E) h:=5352126+182043=5534169, hh:=352 +[=> piyo.] +483: pop +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +484: y0 786432 v:=9306112+786432=10092544, vv:=639 +485: push +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +486: push +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +487: fntnum1 current font is min10 +488: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +491: pop +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +492: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +496: fntnum0 current font is ec-lmr10 +497: setchar48 ("30) h:=630598+327680=958278, hh:=61 +498: setchar120 ("78) h:=958278+345898=1304176, hh:=83 +499: setchar70 ("46) h:=1304176+427818=1731994, hh:=110 +500: setchar70 ("46) h:=1731994+427818=2159812, hh:=137 +501: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149 +[0xFF.] +502: pop +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +503: y0 786432 v:=10092544+786432=10878976, vv:=689 +504: push +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +505: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +509: fntnum1 current font is min10 +510: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +513: fntnum0 current font is ec-lmr10 +514: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +515: w3 218453 h:=2123361+218453=2341814, hh:=148 +519: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +520: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +521: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +523: set1 197("C5) h:=3252044+491520=3743564, hh:=237 +525: set1 255("FF) h:=3743564+327680=4071244, hh:=258 +527: set1 191("BF) h:=4071244+491520=4562764, hh:=289 +529: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301 +530: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336 +531: setchar97 ("61) h:=5290918+327680=5618598, hh:=357 +532: setchar99 ("63) h:=5618598+291275=5909873, hh:=375 +533: setchar114 ("72) h:=5909873+256683=6166556, hh:=391 +534: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412 +535: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424 +536: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438 +537: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470 +[:macro:->] +538: fntnum1 current font is min10 +539: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510 +[␢] +542: fntnum0 current font is ec-lmr10 +543: setchar41 ("29) h:=8035068+254870=8289938, hh:=526 +544: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538 +545: w0 218453 h:=8471981+218453=8690434, hh:=550 +546: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571 +[), \] +547: set1 255("FF) h:=9018114+327680=9345794, hh:=592 +549: set1 197("C5) h:=9345794+491520=9837314, hh:=623 +551: set1 255("FF) h:=9837314+327680=10164994, hh:=644 +553: set1 191("BF) h:=10164994+491520=10656514, hh:=675 +555: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687 +556: w0 218453 h:=10838557+218453=11057010, hh:=700 +557: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735 +558: setchar97 ("61) h:=11603121+327680=11930801, hh:=756 +559: setchar99 ("63) h:=11930801+291275=12222076, hh:=774 +560: setchar114 ("72) h:=12222076+256683=12478759, hh:=790 +561: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811 +562: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823 +563: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837 +564: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869 +565: setchar40 ("28) h:=13716673+254870=13971543, hh:=885 +566: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906 +[, macro:->(\] +567: set1 255("FF) h:=14299223+327680=14626903, hh:=927 +569: set1 197("C5) h:=14626903+491520=15118423, hh:=958 +571: set1 255("FF) h:=15118423+327680=15446103, hh:=979 +573: set1 191("BF) h:=15446103+491520=15937623, hh:=1010 +575: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022 +576: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057 +577: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078 +578: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096 +579: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112 +580: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133 +581: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145 +582: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159 +583: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191 +[:macro:->] +584: fntnum1 current font is min10 +585: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231 +[␢] +588: fntnum0 current font is ec-lmr10 +589: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247 +590: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259 +[).] +591: pop +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +592: y0 786432 v:=10878976+786432=11665408, vv:=739 +593: push +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +594: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +598: fntnum1 current font is min10 +599: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +602: fntnum0 current font is ec-lmr10 +603: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +604: w3 218453 h:=2123361+218453=2341814, hh:=148 +608: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +609: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +610: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +612: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218 +613: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253 +614: setchar97 ("61) h:=3980198+327680=4307878, hh:=274 +615: setchar99 ("63) h:=4307878+291275=4599153, hh:=292 +616: setchar114 ("72) h:=4599153+256683=4855836, hh:=308 +617: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329 +618: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341 +619: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355 +620: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387 +[:macro:->] +621: fntnum1 current font is min10 +622: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427 +[] +625: fntnum0 current font is ec-lmr10 +626: setchar41 ("29) h:=6724348+254870=6979218, hh:=443 +627: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455 +628: w0 218453 h:=7161261+218453=7379714, hh:=467 +629: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488 +[), \] +630: set1 255("FF) h:=7707394+327680=8035074, hh:=509 +632: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521 +633: w0 218453 h:=8217117+218453=8435570, hh:=534 +634: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569 +635: setchar97 ("61) h:=8981681+327680=9309361, hh:=590 +636: setchar99 ("63) h:=9309361+291275=9600636, hh:=608 +637: setchar114 ("72) h:=9600636+256683=9857319, hh:=624 +638: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645 +639: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657 +640: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671 +641: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703 +642: setchar40 ("28) h:=11095233+254870=11350103, hh:=719 +643: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740 +[, macro:->(\] +644: set1 255("FF) h:=11677783+327680=12005463, hh:=761 +646: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773 +647: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808 +648: setchar97 ("61) h:=12733617+327680=13061297, hh:=829 +649: setchar99 ("63) h:=13061297+291275=13352572, hh:=847 +650: setchar114 ("72) h:=13352572+256683=13609255, hh:=863 +651: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884 +652: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896 +653: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910 +654: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942 +[:macro:->] +655: fntnum1 current font is min10 +656: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982 +[] +659: fntnum0 current font is ec-lmr10 +660: setchar41 ("29) h:=15477767+254870=15732637, hh:=998 +661: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010 +[).] +662: pop +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +663: y0 786432 v:=11665408+786432=12451840, vv:=789 +664: push +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +665: push +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +666: fntnum1 current font is min10 +667: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +670: pop +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +671: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +675: fntnum0 current font is ec-lmr10 +676: setchar99 ("63) h:=630598+291275=921873, hh:=58 +677: setchar111 ("6F) h:=921873+327680=1249553, hh:=79 +678: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102 +679: right2 -18205 h:=1613638-18205=1595433, hh:=101 +682: setchar116 ("74) h:=1595433+254870=1850303, hh:=117 +683: setchar101 ("65) h:=1850303+291275=2141578, hh:=135 +684: setchar120 ("78) h:=2141578+345898=2487476, hh:=157 +685: setchar116 ("74) h:=2487476+254870=2742346, hh:=173 +686: setchar115 ("73) h:=2742346+258506=3000852, hh:=189 +687: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201 +[contexts.] +688: pop +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +689: y0 786432 v:=12451840+786432=13238272, vv:=839 +690: push +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +691: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +695: fntnum1 current font is min10 +696: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +699: fntnum0 current font is ec-lmr10 +700: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +702: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +704: w3 218453 h:=2924358+218453=3142811, hh:=199 +708: setchar97 ("61) h:=3142811+327680=3470491, hh:=220 +[ a] +709: set1 197("C5) h:=3470491+491520=3962011, hh:=251 +711: set1 191("BF) h:=3962011+491520=4453531, hh:=282 +713: w0 218453 h:=4453531+218453=4671984, hh:=296 +[ ] +714: set1 197("C5) h:=4671984+491520=5163504, hh:=327 +716: set1 191("BF) h:=5163504+491520=5655024, hh:=358 +718: w0 218453 h:=5655024+218453=5873477, hh:=372 +[ ] +719: fntnum1 current font is min10 +720: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412 +723: w0 218453 h:=6504075+218453=6722528, hh:=426 +[䔿 ] +724: fntnum0 current font is ec-lmr10 +725: set1 197("C5) h:=6722528+491520=7214048, hh:=457 +727: set1 191("BF) h:=7214048+491520=7705568, hh:=488 +729: w0 218453 h:=7705568+218453=7924021, hh:=502 +[ ] +730: set1 197("C5) h:=7924021+491520=8415541, hh:=533 +732: set1 191("BF) h:=8415541+491520=8907061, hh:=564 +734: w0 218453 h:=8907061+218453=9125514, hh:=578 +[ ] +735: fntnum1 current font is min10 +736: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618 +739: x3 436906 h:=9756112+436906=10193018, hh:=646 +[䔿 ] +743: fntnum0 current font is ec-lmr10 +744: set1 197("C5) h:=10193018+491520=10684538, hh:=677 +746: set1 191("BF) h:=10684538+491520=11176058, hh:=708 +748: w0 218453 h:=11176058+218453=11394511, hh:=722 +[ ] +749: fntnum1 current font is min10 +750: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762 +753: x0 436906 h:=12025109+436906=12462015, hh:=789 +754: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829 +[䔿 ] +757: fntnum0 current font is ec-lmr10 +758: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864 +759: setchar97 ("61) h:=13638724+327680=13966404, hh:=885 +760: setchar99 ("63) h:=13966404+291275=14257679, hh:=903 +761: setchar114 ("72) h:=14257679+256683=14514362, hh:=919 +762: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940 +763: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952 +764: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966 +765: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998 +[macro:->] +766: set1 197("C5) h:=15752276+491520=16243796, hh:=1029 +768: set1 191("BF) h:=16243796+491520=16735316, hh:=1060 +770: w0 218453 h:=16735316+218453=16953769, hh:=1074 +[ ] +771: set1 197("C5) h:=16953769+491520=17445289, hh:=1105 +773: set1 191("BF) h:=17445289+491520=17936809, hh:=1136 +775: w0 218453 h:=17936809+218453=18155262, hh:=1150 +[ ] +776: fntnum1 current font is min10 +777: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190 +[䔿] +780: pop +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +781: y0 786432 v:=13238272+786432=14024704, vv:=888 +782: push +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +783: right3 1310720 h:=0+1310720=1310720, hh:=83 +787: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[ ␢] +790: fntnum0 current font is ec-lmr10 +791: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +793: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +795: w3 218453 h:=2924358+218453=3142811, hh:=199 +[ ] +799: set1 197("C5) h:=3142811+491520=3634331, hh:=230 +801: set1 191("BF) h:=3634331+491520=4125851, hh:=261 +803: fntnum1 current font is min10 +804: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301 +[䔿] +807: fntnum0 current font is ec-lmr10 +808: set1 197("C5) h:=4756449+491520=5247969, hh:=332 +810: set1 191("BF) h:=5247969+491520=5739489, hh:=363 +812: w0 218453 h:=5739489+218453=5957942, hh:=377 +813: setchar97 ("61) h:=5957942+327680=6285622, hh:=398 +[ a] +814: set1 197("C5) h:=6285622+491520=6777142, hh:=429 +816: set1 191("BF) h:=6777142+491520=7268662, hh:=460 +818: w0 218453 h:=7268662+218453=7487115, hh:=474 +[ ] +819: set1 197("C5) h:=7487115+491520=7978635, hh:=505 +821: set1 191("BF) h:=7978635+491520=8470155, hh:=536 +823: w0 218453 h:=8470155+218453=8688608, hh:=550 +[ ] +824: fntnum1 current font is min10 +825: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590 +828: w0 218453 h:=9319206+218453=9537659, hh:=604 +[䔿 ] +829: fntnum0 current font is ec-lmr10 +830: set1 197("C5) h:=9537659+491520=10029179, hh:=635 +832: set1 191("BF) h:=10029179+491520=10520699, hh:=666 +834: w0 218453 h:=10520699+218453=10739152, hh:=680 +[ ] +835: set1 197("C5) h:=10739152+491520=11230672, hh:=711 +837: set1 191("BF) h:=11230672+491520=11722192, hh:=742 +839: w0 218453 h:=11722192+218453=11940645, hh:=756 +[ ] +840: fntnum1 current font is min10 +841: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796 +844: right3 436906 h:=12571243+436906=13008149, hh:=824 +[䔿 ] +848: fntnum0 current font is ec-lmr10 +849: set1 197("C5) h:=13008149+491520=13499669, hh:=855 +851: set1 191("BF) h:=13499669+491520=13991189, hh:=886 +853: w0 218453 h:=13991189+218453=14209642, hh:=900 +[ ] +854: fntnum1 current font is min10 +855: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940 +858: w0 218453 h:=14840240+218453=15058693, hh:=954 +859: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994 +[䔿 ] +862: fntnum0 current font is ec-lmr10 +863: set1 255("FF) h:=15689291+327680=16016971, hh:=1015 +865: pop +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +866: y0 786432 v:=14024704+786432=14811136, vv:=938 +867: push +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +868: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +872: fntnum1 current font is min10 +873: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +876: pop +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +877: y0 786432 v:=14811136+786432=15597568, vv:=988 +878: push +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +879: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +883: fntnum0 current font is ec-lmr10 +884: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +885: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +886: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +887: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +888: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +889: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +890: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +891: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +892: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +893: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[.a...c.b..] +894: pop +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +895: y0 786432 v:=15597568+786432=16384000, vv:=1038 +896: push +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +897: right3 1310720 h:=0+1310720=1310720, hh:=83 +901: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +902: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +903: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +904: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +905: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +906: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +907: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +908: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +909: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +910: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[ .a...c.b..] +911: pop +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +912: y0 786432 v:=16384000+786432=17170432, vv:=1088 +913: push +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +914: push +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +915: fntnum1 current font is min10 +916: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +919: pop +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +920: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +924: fntnum0 current font is ec-lmr10 +925: setchar99 ("63) h:=630598+291275=921873, hh:=58 +926: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +927: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +928: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +929: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +930: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +931: setchar50 ("32) h:=2709530+327680=3037210, hh:=192 +932: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname2.] +933: pop +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +934: y0 786432 v:=17170432+786432=17956864, vv:=1137 +935: push +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +936: right3 1310720 h:=0+1310720=1310720, hh:=83 +940: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +941: setchar72 ("48) h:=1674805+491520=2166325, hh:=137 +942: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169 +943: right2 -18205 h:=2676063-18205=2657858, hh:=168 +946: setchar87 ("57) h:=2657858+673578=3331436, hh:=211 +947: right3 218453 h:=3331436+218453=3549889, hh:=225 +951: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237 +952: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249 +953: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261 +[ SHOW ...] +954: pop +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +955: y0 786432 v:=17956864+786432=18743296, vv:=1187 +956: push +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +957: right3 1310720 h:=0+1310720=1310720, hh:=83 +961: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +962: setchar84 ("54) h:=1674805+473301=2148106, hh:=136 +963: setchar82 ("52) h:=2148106+482410=2630516, hh:=167 +964: setchar73 ("49) h:=2630516+236650=2867166, hh:=182 +965: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213 +966: setchar71 ("47) h:=3358686+514261=3872947, hh:=246 +967: right3 218453 h:=3872947+218453=4091400, hh:=259 +971: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280 +[ STRING \] +972: set1 197("C5) h:=4419080+491520=4910600, hh:=311 +974: set1 191("BF) h:=4910600+491520=5402120, hh:=342 +976: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354 +977: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375 +[.\] +978: fntnum1 current font is min10 +979: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415 +[䔿] +982: fntnum0 current font is ec-lmr10 +983: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427 +984: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448 +[.\] +985: set1 197("C5) h:=7052164+491520=7543684, hh:=479 +987: set1 191("BF) h:=7543684+491520=8035204, hh:=510 +989: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522 +990: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543 +[.\] +991: fntnum1 current font is min10 +992: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583 +[䔿] +995: pop +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +996: y0 786432 v:=18743296+786432=19529728, vv:=1237 +997: push +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +998: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +1002: fntnum0 current font is ec-lmr10 +1003: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121 +1004: setchar69 ("45) h:=1911488+446005=2357493, hh:=149 +1005: setchar65 ("41) h:=2357493+491520=2849013, hh:=180 +1006: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211 +1007: setchar73 ("49) h:=3340533+236650=3577183, hh:=226 +1008: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257 +1009: setchar71 ("47) h:=4068703+514261=4582964, hh:=290 +1010: right3 218453 h:=4582964+218453=4801417, hh:=304 +1014: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339 +1015: setchar97 ("61) h:=5347528+327680=5675208, hh:=360 +1016: setchar99 ("63) h:=5675208+291275=5966483, hh:=378 +1017: setchar114 ("72) h:=5966483+256683=6223166, hh:=394 +1018: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415 +1019: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427 +1020: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441 +1021: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473 +1022: setchar97 ("61) h:=7461080+327680=7788760, hh:=494 +1023: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506 +1024: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541 +1025: setchar97 ("61) h:=8516914+327680=8844594, hh:=562 +1026: setchar99 ("63) h:=8844594+291275=9135869, hh:=580 +1027: setchar114 ("72) h:=9135869+256683=9392552, hh:=596 +1028: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617 +1029: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629 +1030: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643 +1031: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675 +1032: setchar98 ("62) h:=10630466+364085=10994551, hh:=698 +1033: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710 +1034: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745 +1035: setchar97 ("61) h:=11722705+327680=12050385, hh:=765 +1036: setchar99 ("63) h:=12050385+291275=12341660, hh:=783 +1037: setchar114 ("72) h:=12341660+256683=12598343, hh:=799 +1038: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820 +1039: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832 +1040: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846 +1041: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878 +1042: setchar97 ("61) h:=13836257+327680=14163937, hh:=899 +1043: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911 +1044: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945 +1045: setchar97 ("61) h:=14892091+327680=15219771, hh:=966 +1046: setchar99 ("63) h:=15219771+291275=15511046, hh:=984 +1047: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000 +1048: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021 +1049: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033 +1050: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047 +1051: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079 +1052: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102 +1053: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114 +[MEANING macro:->a.macro:->b.macro:->a.macro:->b.] +1054: pop +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +1055: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1056: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +1060: eop diff --git a/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ new file mode 100644 index 00000000000..d126d4c505e --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/eup-pknj-ua.typ @@ -0,0 +1,829 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 1062. +maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1 +Font 1: umin10 (JFM)---loaded at size 655360 DVI units +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 39003 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: ec-lmr10 +133: fntnum0 current font is ec-lmr10 +134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114 +135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126 +136: w3 218453 h:=1984283+218453=2202736, hh:=140 +[A: ] +140: set1 197("C5) h:=2202736+491520=2694256, hh:=171 +142: set1 191("BF) h:=2694256+491520=3185776, hh:=202 +144: w0 218453 h:=3185776+218453=3404229, hh:=216 +[ ] +145: set1 197("C5) h:=3404229+491520=3895749, hh:=247 +147: set1 191("BF) h:=3895749+491520=4387269, hh:=278 +149: w0 218453 h:=4387269+218453=4605722, hh:=292 +[ ] +150: fntdef1 1: umin10 +172: fntnum1 current font is umin10 +173: set2 39003("985B) type=0 h:=4605722+630598=5236320, hh:=332 +[顛] +176: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +177: y3 786432 v:=655360+786432=1441792, vv:=91 +181: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +182: push +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +183: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +186: pop +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +187: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +191: fntnum0 current font is ec-lmr10 +192: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +193: setchar109 ("6D) h:=958278+546111=1504389, hh:=96 +194: setchar101 ("65) h:=1504389+291275=1795664, hh:=114 +195: setchar97 ("61) h:=1795664+327680=2123344, hh:=135 +196: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158 +197: setchar105 ("69) h:=2487429+182043=2669472, hh:=170 +198: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193 +199: setchar103 ("67) h:=3033557+327680=3361237, hh:=214 +200: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226 +[\meaning.] +201: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +202: y0 786432 v:=1441792+786432=2228224, vv:=141 +203: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +204: right3 1310720 h:=0+1310720=1310720, hh:=83 +208: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +209: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +210: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +211: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +212: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +213: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +214: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +215: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +216: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287 +217: setchar97 ("61) h:=4516494+327680=4844174, hh:=308 +218: setchar99 ("63) h:=4844174+291275=5135449, hh:=326 +219: setchar114 ("72) h:=5135449+256683=5392132, hh:=342 +220: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363 +221: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375 +222: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389 +223: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421 +[ macro:->macro:->] +224: set1 197("C5) h:=6630046+491520=7121566, hh:=452 +226: set1 191("BF) h:=7121566+491520=7613086, hh:=483 +228: w3 218453 h:=7613086+218453=7831539, hh:=496 +[ ] +232: set1 197("C5) h:=7831539+491520=8323059, hh:=527 +234: set1 191("BF) h:=8323059+491520=8814579, hh:=558 +236: w0 218453 h:=8814579+218453=9033032, hh:=572 +[ ] +237: fntnum1 current font is umin10 +238: set2 39003("985B) type=0 h:=9033032+630598=9663630, hh:=612 +[顛] +241: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +242: y0 786432 v:=2228224+786432=3014656, vv:=191 +243: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +244: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +248: fntnum0 current font is ec-lmr10 +249: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +250: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +251: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +252: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +253: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +254: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +255: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +256: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +[macro:->] +257: set1 197("C5) h:=3970383+491520=4461903, hh:=283 +259: set1 191("BF) h:=4461903+491520=4953423, hh:=314 +261: w3 218453 h:=4953423+218453=5171876, hh:=328 +[ ] +265: set1 197("C5) h:=5171876+491520=5663396, hh:=359 +267: set1 191("BF) h:=5663396+491520=6154916, hh:=390 +269: w0 218453 h:=6154916+218453=6373369, hh:=404 +[ ] +270: fntnum1 current font is umin10 +271: set2 39003("985B) type=0 h:=6373369+630598=7003967, hh:=444 +[顛] +274: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +275: y0 786432 v:=3014656+786432=3801088, vv:=241 +276: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +277: push +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +278: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +281: pop +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +282: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +286: fntnum0 current font is ec-lmr10 +287: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +288: setchar106 ("6A) h:=958278+200245=1158523, hh:=74 +289: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95 +290: setchar98 ("62) h:=1486203+364085=1850288, hh:=118 +291: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141 +292: setchar97 ("61) h:=2214373+327680=2542053, hh:=162 +293: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197 +294: setchar101 ("65) h:=3088164+291275=3379439, hh:=215 +295: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227 +[\jobname.] +296: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +297: y0 786432 v:=3801088+786432=4587520, vv:=291 +298: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +299: push +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +300: fntnum1 current font is umin10 +301: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +304: pop +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +305: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +309: fntnum0 current font is ec-lmr10 +310: setchar111 ("6F) h:=630598+327680=958278, hh:=61 +311: setchar110 ("6E) h:=958278+364085=1322363, hh:=84 +312: setchar101 ("65) h:=1322363+291275=1613638, hh:=102 +313: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114 +314: setchar101 ("65) h:=1795681+291275=2086956, hh:=132 +315: setchar116 ("74) h:=2086956+254870=2341826, hh:=148 +316: setchar116 ("74) h:=2341826+254870=2596696, hh:=164 +317: setchar101 ("65) h:=2596696+291275=2887971, hh:=182 +318: setchar114 ("72) h:=2887971+256683=3144654, hh:=198 +319: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210 +[oneletter.] +320: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +321: y0 786432 v:=4587520+786432=5373952, vv:=340 +322: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +323: right3 1310720 h:=0+1310720=1310720, hh:=83 +327: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +328: set1 196("C4) h:=1638400+491520=2129920, hh:=135 +330: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147 +331: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168 +[.\] +332: set1 197("C5) h:=2639643+491520=3131163, hh:=199 +334: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211 +335: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232 +[.\] +336: set1 255("FF) h:=3640886+327680=3968566, hh:=253 +338: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265 +339: right3 291271 h:=4150609+291271=4441880, hh:=281 +[. ] +343: set1 191("BF) h:=4441880+491520=4933400, hh:=312 +345: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +346: y0 786432 v:=5373952+786432=6160384, vv:=390 +347: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +348: push +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +349: fntnum1 current font is umin10 +350: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +353: pop +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +354: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +358: fntnum0 current font is ec-lmr10 +359: setchar99 ("63) h:=630598+291275=921873, hh:=58 +360: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +361: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +362: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +363: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +364: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +365: setchar49 ("31) h:=2709530+327680=3037210, hh:=192 +366: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname1.] +367: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +368: y0 786432 v:=6160384+786432=6946816, vv:=440 +369: push +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +370: right3 1310720 h:=0+1310720=1310720, hh:=83 +374: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +375: set1 197("C5) h:=1638400+491520=2129920, hh:=135 +377: set1 191("BF) h:=2129920+491520=2621440, hh:=166 +379: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187 +[\] +380: set1 197("C5) h:=2949120+491520=3440640, hh:=218 +382: set1 191("BF) h:=3440640+491520=3932160, hh:=249 +384: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261 +385: w3 218453 h:=4114203+218453=4332656, hh:=274 +389: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295 +[, \] +390: set1 197("C5) h:=4660336+491520=5151856, hh:=326 +392: set1 191("BF) h:=5151856+491520=5643376, hh:=357 +394: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369 +395: w0 218453 h:=5825419+218453=6043872, hh:=383 +396: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404 +[, \] +397: fntnum1 current font is umin10 +398: set2 39003("985B) type=0 h:=6371552+630598=7002150, hh:=444 +[顛] +401: pop +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +402: y0 786432 v:=6946816+786432=7733248, vv:=490 +403: push +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +404: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +408: fntnum0 current font is ec-lmr10 +409: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[\] +410: fntnum1 current font is umin10 +411: set2 12354("3042) type=0 h:=1638400+630598=2268998, hh:=144 +[あ] +414: fntnum0 current font is ec-lmr10 +415: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176 +416: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208 +417: right3 218453 h:=3288474+218453=3506927, hh:=222 +421: setchar104 ("68) h:=3506927+364085=3871012, hh:=245 +422: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266 +423: setchar103 ("67) h:=4198692+327680=4526372, hh:=287 +424: setchar101 ("65) h:=4526372+291275=4817647, hh:=305 +425: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317 +[=> hoge,] +426: pop +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +427: y0 786432 v:=7733248+786432=8519680, vv:=540 +428: push +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +429: right3 1310720 h:=0+1310720=1310720, hh:=83 +433: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +434: set1 227("E3) h:=1638400+327680=1966080, hh:=125 +436: set1 129("81) h:=1966080+491520=2457600, hh:=156 +438: w2 -18205 h:=2457600-18205=2439395, hh:=155 +441: set1 130("82) h:=2439395+473301=2912696, hh:=185 +443: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217 +444: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249 +445: right3 218453 h:=3932172+218453=4150625, hh:=263 +449: setchar112 ("70) h:=4150625+364085=4514710, hh:=286 +450: setchar105 ("69) h:=4514710+182043=4696753, hh:=298 +451: setchar121 ("79) h:=4696753+345898=5042651, hh:=320 +452: w0 -18205 h:=5042651-18205=5024446, hh:=319 +453: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340 +454: setchar44 ("2C) h:=5352126+182043=5534169, hh:=352 +[=> piyo,] +455: pop +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +456: y0 786432 v:=8519680+786432=9306112, vv:=589 +457: push +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +458: right3 1310720 h:=0+1310720=1310720, hh:=83 +462: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +463: set1 227("E3) h:=1638400+327680=1966080, hh:=125 +465: set1 129("81) h:=1966080+491520=2457600, hh:=156 +467: w2 -18205 h:=2457600-18205=2439395, hh:=155 +470: set1 130("82) h:=2439395+473301=2912696, hh:=185 +472: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217 +473: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249 +474: right3 218453 h:=3932172+218453=4150625, hh:=263 +478: setchar112 ("70) h:=4150625+364085=4514710, hh:=286 +479: setchar105 ("69) h:=4514710+182043=4696753, hh:=298 +480: setchar121 ("79) h:=4696753+345898=5042651, hh:=320 +481: w0 -18205 h:=5042651-18205=5024446, hh:=319 +482: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340 +483: setchar46 ("2E) h:=5352126+182043=5534169, hh:=352 +[=> piyo.] +484: pop +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +485: y0 786432 v:=9306112+786432=10092544, vv:=639 +486: push +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +487: push +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +488: fntnum1 current font is umin10 +489: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +492: pop +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +493: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +497: fntnum0 current font is ec-lmr10 +498: setchar48 ("30) h:=630598+327680=958278, hh:=61 +499: setchar120 ("78) h:=958278+345898=1304176, hh:=83 +500: setchar70 ("46) h:=1304176+427818=1731994, hh:=110 +501: setchar70 ("46) h:=1731994+427818=2159812, hh:=137 +502: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149 +[0xFF.] +503: pop +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +504: y0 786432 v:=10092544+786432=10878976, vv:=689 +505: push +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +506: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +510: fntnum1 current font is umin10 +511: set2 12354("3042) type=0 h:=1310720+630598=1941318, hh:=123 +[あ] +514: fntnum0 current font is ec-lmr10 +515: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +516: w3 218453 h:=2123361+218453=2341814, hh:=148 +520: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +521: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +522: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +524: set1 197("C5) h:=3252044+491520=3743564, hh:=237 +526: set1 255("FF) h:=3743564+327680=4071244, hh:=258 +528: set1 191("BF) h:=4071244+491520=4562764, hh:=289 +530: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301 +531: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336 +532: setchar97 ("61) h:=5290918+327680=5618598, hh:=357 +533: setchar99 ("63) h:=5618598+291275=5909873, hh:=375 +534: setchar114 ("72) h:=5909873+256683=6166556, hh:=391 +535: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412 +536: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424 +537: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438 +538: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470 +[:macro:->] +539: fntnum1 current font is umin10 +540: set2 12354("3042) type=0 h:=7404470+630598=8035068, hh:=510 +[あ] +543: fntnum0 current font is ec-lmr10 +544: setchar41 ("29) h:=8035068+254870=8289938, hh:=526 +545: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538 +546: w0 218453 h:=8471981+218453=8690434, hh:=550 +547: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571 +[), \] +548: set1 255("FF) h:=9018114+327680=9345794, hh:=592 +550: set1 197("C5) h:=9345794+491520=9837314, hh:=623 +552: set1 255("FF) h:=9837314+327680=10164994, hh:=644 +554: set1 191("BF) h:=10164994+491520=10656514, hh:=675 +556: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687 +557: w0 218453 h:=10838557+218453=11057010, hh:=700 +558: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735 +559: setchar97 ("61) h:=11603121+327680=11930801, hh:=756 +560: setchar99 ("63) h:=11930801+291275=12222076, hh:=774 +561: setchar114 ("72) h:=12222076+256683=12478759, hh:=790 +562: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811 +563: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823 +564: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837 +565: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869 +566: setchar40 ("28) h:=13716673+254870=13971543, hh:=885 +567: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906 +[, macro:->(\] +568: set1 255("FF) h:=14299223+327680=14626903, hh:=927 +570: set1 197("C5) h:=14626903+491520=15118423, hh:=958 +572: set1 255("FF) h:=15118423+327680=15446103, hh:=979 +574: set1 191("BF) h:=15446103+491520=15937623, hh:=1010 +576: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022 +577: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057 +578: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078 +579: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096 +580: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112 +581: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133 +582: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145 +583: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159 +584: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191 +[:macro:->] +585: fntnum1 current font is umin10 +586: set2 12354("3042) type=0 h:=18779329+630598=19409927, hh:=1231 +[あ] +589: fntnum0 current font is ec-lmr10 +590: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247 +591: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259 +[).] +592: pop +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +593: y0 786432 v:=10878976+786432=11665408, vv:=739 +594: push +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +595: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +599: fntnum1 current font is umin10 +600: set2 12356("3044) type=0 h:=1310720+630598=1941318, hh:=123 +[い] +603: fntnum0 current font is ec-lmr10 +604: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +605: w3 218453 h:=2123361+218453=2341814, hh:=148 +609: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +610: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +611: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +613: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218 +614: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253 +615: setchar97 ("61) h:=3980198+327680=4307878, hh:=274 +616: setchar99 ("63) h:=4307878+291275=4599153, hh:=292 +617: setchar114 ("72) h:=4599153+256683=4855836, hh:=308 +618: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329 +619: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341 +620: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355 +621: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387 +[:macro:->] +622: fntnum1 current font is umin10 +623: set2 12356("3044) type=0 h:=6093750+630598=6724348, hh:=427 +[い] +626: fntnum0 current font is ec-lmr10 +627: setchar41 ("29) h:=6724348+254870=6979218, hh:=443 +628: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455 +629: w0 218453 h:=7161261+218453=7379714, hh:=467 +630: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488 +[), \] +631: set1 255("FF) h:=7707394+327680=8035074, hh:=509 +633: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521 +634: w0 218453 h:=8217117+218453=8435570, hh:=534 +635: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569 +636: setchar97 ("61) h:=8981681+327680=9309361, hh:=590 +637: setchar99 ("63) h:=9309361+291275=9600636, hh:=608 +638: setchar114 ("72) h:=9600636+256683=9857319, hh:=624 +639: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645 +640: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657 +641: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671 +642: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703 +643: setchar40 ("28) h:=11095233+254870=11350103, hh:=719 +644: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740 +[, macro:->(\] +645: set1 255("FF) h:=11677783+327680=12005463, hh:=761 +647: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773 +648: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808 +649: setchar97 ("61) h:=12733617+327680=13061297, hh:=829 +650: setchar99 ("63) h:=13061297+291275=13352572, hh:=847 +651: setchar114 ("72) h:=13352572+256683=13609255, hh:=863 +652: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884 +653: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896 +654: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910 +655: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942 +[:macro:->] +656: fntnum1 current font is umin10 +657: set2 12356("3044) type=0 h:=14847169+630598=15477767, hh:=982 +[い] +660: fntnum0 current font is ec-lmr10 +661: setchar41 ("29) h:=15477767+254870=15732637, hh:=998 +662: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010 +[).] +663: pop +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +664: y0 786432 v:=11665408+786432=12451840, vv:=789 +665: push +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +666: push +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +667: fntnum1 current font is umin10 +668: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +671: pop +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +672: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +676: fntnum0 current font is ec-lmr10 +677: setchar99 ("63) h:=630598+291275=921873, hh:=58 +678: setchar111 ("6F) h:=921873+327680=1249553, hh:=79 +679: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102 +680: right2 -18205 h:=1613638-18205=1595433, hh:=101 +683: setchar116 ("74) h:=1595433+254870=1850303, hh:=117 +684: setchar101 ("65) h:=1850303+291275=2141578, hh:=135 +685: setchar120 ("78) h:=2141578+345898=2487476, hh:=157 +686: setchar116 ("74) h:=2487476+254870=2742346, hh:=173 +687: setchar115 ("73) h:=2742346+258506=3000852, hh:=189 +688: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201 +[contexts.] +689: pop +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +690: y0 786432 v:=12451840+786432=13238272, vv:=839 +691: push +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +692: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +696: fntnum1 current font is umin10 +697: set2 12354("3042) type=0 h:=1310720+630598=1941318, hh:=123 +[あ] +700: fntnum0 current font is ec-lmr10 +701: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +703: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +705: w3 218453 h:=2924358+218453=3142811, hh:=199 +709: setchar97 ("61) h:=3142811+327680=3470491, hh:=220 +[ a] +710: set1 197("C5) h:=3470491+491520=3962011, hh:=251 +712: set1 191("BF) h:=3962011+491520=4453531, hh:=282 +714: w0 218453 h:=4453531+218453=4671984, hh:=296 +[ ] +715: set1 197("C5) h:=4671984+491520=5163504, hh:=327 +717: set1 191("BF) h:=5163504+491520=5655024, hh:=358 +719: w0 218453 h:=5655024+218453=5873477, hh:=372 +[ ] +720: fntnum1 current font is umin10 +721: set2 39003("985B) type=0 h:=5873477+630598=6504075, hh:=412 +724: w0 218453 h:=6504075+218453=6722528, hh:=426 +[顛 ] +725: fntnum0 current font is ec-lmr10 +726: set1 197("C5) h:=6722528+491520=7214048, hh:=457 +728: set1 191("BF) h:=7214048+491520=7705568, hh:=488 +730: w0 218453 h:=7705568+218453=7924021, hh:=502 +[ ] +731: set1 197("C5) h:=7924021+491520=8415541, hh:=533 +733: set1 191("BF) h:=8415541+491520=8907061, hh:=564 +735: w0 218453 h:=8907061+218453=9125514, hh:=578 +[ ] +736: fntnum1 current font is umin10 +737: set2 39003("985B) type=0 h:=9125514+630598=9756112, hh:=618 +740: x3 436906 h:=9756112+436906=10193018, hh:=646 +[顛 ] +744: fntnum0 current font is ec-lmr10 +745: set1 197("C5) h:=10193018+491520=10684538, hh:=677 +747: set1 191("BF) h:=10684538+491520=11176058, hh:=708 +749: w0 218453 h:=11176058+218453=11394511, hh:=722 +[ ] +750: fntnum1 current font is umin10 +751: set2 39003("985B) type=0 h:=11394511+630598=12025109, hh:=762 +754: x0 436906 h:=12025109+436906=12462015, hh:=789 +755: set2 12356("3044) type=0 h:=12462015+630598=13092613, hh:=829 +[顛 い] +758: fntnum0 current font is ec-lmr10 +759: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864 +760: setchar97 ("61) h:=13638724+327680=13966404, hh:=885 +761: setchar99 ("63) h:=13966404+291275=14257679, hh:=903 +762: setchar114 ("72) h:=14257679+256683=14514362, hh:=919 +763: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940 +764: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952 +765: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966 +766: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998 +[macro:->] +767: set1 197("C5) h:=15752276+491520=16243796, hh:=1029 +769: set1 191("BF) h:=16243796+491520=16735316, hh:=1060 +771: w0 218453 h:=16735316+218453=16953769, hh:=1074 +[ ] +772: set1 197("C5) h:=16953769+491520=17445289, hh:=1105 +774: set1 191("BF) h:=17445289+491520=17936809, hh:=1136 +776: w0 218453 h:=17936809+218453=18155262, hh:=1150 +[ ] +777: fntnum1 current font is umin10 +778: set2 39003("985B) type=0 h:=18155262+630598=18785860, hh:=1190 +[顛] +781: pop +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +782: y0 786432 v:=13238272+786432=14024704, vv:=888 +783: push +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +784: right3 1310720 h:=0+1310720=1310720, hh:=83 +788: set2 12354("3042) type=0 h:=1310720+630598=1941318, hh:=123 +[ あ] +791: fntnum0 current font is ec-lmr10 +792: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +794: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +796: w3 218453 h:=2924358+218453=3142811, hh:=199 +[ ] +800: set1 197("C5) h:=3142811+491520=3634331, hh:=230 +802: set1 191("BF) h:=3634331+491520=4125851, hh:=261 +804: fntnum1 current font is umin10 +805: set2 39003("985B) type=0 h:=4125851+630598=4756449, hh:=301 +[顛] +808: fntnum0 current font is ec-lmr10 +809: set1 197("C5) h:=4756449+491520=5247969, hh:=332 +811: set1 191("BF) h:=5247969+491520=5739489, hh:=363 +813: w0 218453 h:=5739489+218453=5957942, hh:=377 +814: setchar97 ("61) h:=5957942+327680=6285622, hh:=398 +[ a] +815: set1 197("C5) h:=6285622+491520=6777142, hh:=429 +817: set1 191("BF) h:=6777142+491520=7268662, hh:=460 +819: w0 218453 h:=7268662+218453=7487115, hh:=474 +[ ] +820: set1 197("C5) h:=7487115+491520=7978635, hh:=505 +822: set1 191("BF) h:=7978635+491520=8470155, hh:=536 +824: w0 218453 h:=8470155+218453=8688608, hh:=550 +[ ] +825: fntnum1 current font is umin10 +826: set2 39003("985B) type=0 h:=8688608+630598=9319206, hh:=590 +829: w0 218453 h:=9319206+218453=9537659, hh:=604 +[顛 ] +830: fntnum0 current font is ec-lmr10 +831: set1 197("C5) h:=9537659+491520=10029179, hh:=635 +833: set1 191("BF) h:=10029179+491520=10520699, hh:=666 +835: w0 218453 h:=10520699+218453=10739152, hh:=680 +[ ] +836: set1 197("C5) h:=10739152+491520=11230672, hh:=711 +838: set1 191("BF) h:=11230672+491520=11722192, hh:=742 +840: w0 218453 h:=11722192+218453=11940645, hh:=756 +[ ] +841: fntnum1 current font is umin10 +842: set2 39003("985B) type=0 h:=11940645+630598=12571243, hh:=796 +845: right3 436906 h:=12571243+436906=13008149, hh:=824 +[顛 ] +849: fntnum0 current font is ec-lmr10 +850: set1 197("C5) h:=13008149+491520=13499669, hh:=855 +852: set1 191("BF) h:=13499669+491520=13991189, hh:=886 +854: w0 218453 h:=13991189+218453=14209642, hh:=900 +[ ] +855: fntnum1 current font is umin10 +856: set2 39003("985B) type=0 h:=14209642+630598=14840240, hh:=940 +859: w0 218453 h:=14840240+218453=15058693, hh:=954 +860: set2 12356("3044) type=0 h:=15058693+630598=15689291, hh:=994 +[顛 い] +863: fntnum0 current font is ec-lmr10 +864: set1 255("FF) h:=15689291+327680=16016971, hh:=1015 +866: pop +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +867: y0 786432 v:=14024704+786432=14811136, vv:=938 +868: push +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +869: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +873: fntnum1 current font is umin10 +874: set2 12356("3044) type=0 h:=1310720+630598=1941318, hh:=123 +[い] +877: pop +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +878: y0 786432 v:=14811136+786432=15597568, vv:=988 +879: push +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +880: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +884: fntnum0 current font is ec-lmr10 +885: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +886: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +887: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +888: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +889: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +890: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +891: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +892: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +893: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +894: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[.a...c.b..] +895: pop +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +896: y0 786432 v:=15597568+786432=16384000, vv:=1038 +897: push +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +898: right3 1310720 h:=0+1310720=1310720, hh:=83 +902: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +903: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +904: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +905: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +906: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +907: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +908: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +909: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +910: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +911: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[ .a...c.b..] +912: pop +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +913: y0 786432 v:=16384000+786432=17170432, vv:=1088 +914: push +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +915: push +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +916: fntnum1 current font is umin10 +917: set2 9632("25A0) type=0 h:=0+630598=630598, hh:=40 +[■] +920: pop +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +921: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +925: fntnum0 current font is ec-lmr10 +926: setchar99 ("63) h:=630598+291275=921873, hh:=58 +927: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +928: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +929: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +930: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +931: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +932: setchar50 ("32) h:=2709530+327680=3037210, hh:=192 +933: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname2.] +934: pop +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +935: y0 786432 v:=17170432+786432=17956864, vv:=1137 +936: push +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +937: right3 1310720 h:=0+1310720=1310720, hh:=83 +941: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +942: setchar72 ("48) h:=1674805+491520=2166325, hh:=137 +943: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169 +944: right2 -18205 h:=2676063-18205=2657858, hh:=168 +947: setchar87 ("57) h:=2657858+673578=3331436, hh:=211 +948: right3 218453 h:=3331436+218453=3549889, hh:=225 +952: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237 +953: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249 +954: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261 +[ SHOW ...] +955: pop +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +956: y0 786432 v:=17956864+786432=18743296, vv:=1187 +957: push +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +958: right3 1310720 h:=0+1310720=1310720, hh:=83 +962: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +963: setchar84 ("54) h:=1674805+473301=2148106, hh:=136 +964: setchar82 ("52) h:=2148106+482410=2630516, hh:=167 +965: setchar73 ("49) h:=2630516+236650=2867166, hh:=182 +966: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213 +967: setchar71 ("47) h:=3358686+514261=3872947, hh:=246 +968: right3 218453 h:=3872947+218453=4091400, hh:=259 +972: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280 +[ STRING \] +973: set1 197("C5) h:=4419080+491520=4910600, hh:=311 +975: set1 191("BF) h:=4910600+491520=5402120, hh:=342 +977: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354 +978: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375 +[.\] +979: fntnum1 current font is umin10 +980: set2 39003("985B) type=0 h:=5911843+630598=6542441, hh:=415 +[顛] +983: fntnum0 current font is ec-lmr10 +984: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427 +985: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448 +[.\] +986: set1 197("C5) h:=7052164+491520=7543684, hh:=479 +988: set1 191("BF) h:=7543684+491520=8035204, hh:=510 +990: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522 +991: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543 +[.\] +992: fntnum1 current font is umin10 +993: set2 39003("985B) type=0 h:=8544927+630598=9175525, hh:=583 +[顛] +996: pop +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +997: y0 786432 v:=18743296+786432=19529728, vv:=1237 +998: push +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +999: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +1003: fntnum0 current font is ec-lmr10 +1004: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121 +1005: setchar69 ("45) h:=1911488+446005=2357493, hh:=149 +1006: setchar65 ("41) h:=2357493+491520=2849013, hh:=180 +1007: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211 +1008: setchar73 ("49) h:=3340533+236650=3577183, hh:=226 +1009: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257 +1010: setchar71 ("47) h:=4068703+514261=4582964, hh:=290 +1011: right3 218453 h:=4582964+218453=4801417, hh:=304 +1015: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339 +1016: setchar97 ("61) h:=5347528+327680=5675208, hh:=360 +1017: setchar99 ("63) h:=5675208+291275=5966483, hh:=378 +1018: setchar114 ("72) h:=5966483+256683=6223166, hh:=394 +1019: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415 +1020: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427 +1021: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441 +1022: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473 +1023: setchar97 ("61) h:=7461080+327680=7788760, hh:=494 +1024: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506 +1025: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541 +1026: setchar97 ("61) h:=8516914+327680=8844594, hh:=562 +1027: setchar99 ("63) h:=8844594+291275=9135869, hh:=580 +1028: setchar114 ("72) h:=9135869+256683=9392552, hh:=596 +1029: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617 +1030: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629 +1031: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643 +1032: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675 +1033: setchar98 ("62) h:=10630466+364085=10994551, hh:=698 +1034: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710 +1035: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745 +1036: setchar97 ("61) h:=11722705+327680=12050385, hh:=765 +1037: setchar99 ("63) h:=12050385+291275=12341660, hh:=783 +1038: setchar114 ("72) h:=12341660+256683=12598343, hh:=799 +1039: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820 +1040: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832 +1041: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846 +1042: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878 +1043: setchar97 ("61) h:=13836257+327680=14163937, hh:=899 +1044: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911 +1045: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945 +1046: setchar97 ("61) h:=14892091+327680=15219771, hh:=966 +1047: setchar99 ("63) h:=15219771+291275=15511046, hh:=984 +1048: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000 +1049: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021 +1050: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033 +1051: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047 +1052: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079 +1053: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102 +1054: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114 +[MEANING macro:->a.macro:->b.macro:->a.macro:->b.] +1055: pop +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +1056: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1057: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +1061: eop diff --git a/Build/source/texk/web2c/euptexdir/tests/free_ixsp.log b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.log new file mode 100644 index 00000000000..05639d0ffdc --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.log @@ -0,0 +1,29 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-free_ixsp.tex +(./eup-free_ixsp.tex +\@tempcnta=\count26 +\@tempcntb=\count27 +\@tempcntc=\count28 +\CNT=\count29 +\CNTA=\count30 +0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. +22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. +41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. +60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. +79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. +98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. +114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. +129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. +144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. +159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. +174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. +189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. +204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. +219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. +234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. +249. 250. 251. 252. 253. 254. 255. +\CNTB=\count31 + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/free_ixsp.tex b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.tex index 9a3b2766415..4fb69d84406 100644 --- a/Build/source/texk/web2c/ptexdir/tests/free_ixsp.tex +++ b/Build/source/texk/web2c/euptexdir/tests/free_ixsp.tex @@ -1,7 +1,8 @@ %#!eptex -ini -etex -\let\dump\relax -\batchmode -\input plain +%\let\dump\relax +%\batchmode +%\input plain +\font\cmr=cmr10\cmr \errorstopmode \catcode`@=11 diff --git a/Build/source/texk/web2c/euptexdir/tests/free_pena.log b/Build/source/texk/web2c/euptexdir/tests/free_pena.log new file mode 100644 index 00000000000..73d7df615a4 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/free_pena.log @@ -0,0 +1,29 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-free_pena.tex +(./eup-free_pena.tex +\@tempcnta=\count26 +\@tempcntb=\count27 +\@tempcntc=\count28 +\CNT=\count29 +\CNTA=\count30 +0. 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. +22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. +41. 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. +60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. 72. 73. 74. 75. 76. 77. 78. +79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. 91. 92. 93. 94. 95. 96. 97. +98. 99. 100. 101. 102. 103. 104. 105. 106. 107. 108. 109. 110. 111. 112. 113. +114. 115. 116. 117. 118. 119. 120. 121. 122. 123. 124. 125. 126. 127. 128. +129. 130. 131. 132. 133. 134. 135. 136. 137. 138. 139. 140. 141. 142. 143. +144. 145. 146. 147. 148. 149. 150. 151. 152. 153. 154. 155. 156. 157. 158. +159. 160. 161. 162. 163. 164. 165. 166. 167. 168. 169. 170. 171. 172. 173. +174. 175. 176. 177. 178. 179. 180. 181. 182. 183. 184. 185. 186. 187. 188. +189. 190. 191. 192. 193. 194. 195. 196. 197. 198. 199. 200. 201. 202. 203. +204. 205. 206. 207. 208. 209. 210. 211. 212. 213. 214. 215. 216. 217. 218. +219. 220. 221. 222. 223. 224. 225. 226. 227. 228. 229. 230. 231. 232. 233. +234. 235. 236. 237. 238. 239. 240. 241. 242. 243. 244. 245. 246. 247. 248. +249. 250. 251. 252. 253. 254. 255. +\CNTB=\count31 + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/free_pena.tex b/Build/source/texk/web2c/euptexdir/tests/free_pena.tex index 184720a80a2..f7d2b51bcad 100644 --- a/Build/source/texk/web2c/ptexdir/tests/free_pena.tex +++ b/Build/source/texk/web2c/euptexdir/tests/free_pena.tex @@ -1,7 +1,15 @@ %#!eptex -ini -etex -\let\dump\relax -\batchmode -\input plain +%\let\dump\relax +%\batchmode +%\input plain +\font\cmr=cmr10\cmr +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi \errorstopmode \catcode`@=11 diff --git a/Build/source/texk/web2c/ptexdir/tests/inhibitglue.tex b/Build/source/texk/web2c/euptexdir/tests/inhibitglue.tex index 2c76a9782a8..6dd55dbdadf 100644 --- a/Build/source/texk/web2c/ptexdir/tests/inhibitglue.tex +++ b/Build/source/texk/web2c/euptexdir/tests/inhibitglue.tex @@ -1,4 +1,14 @@ %#!ptex2pdf -e inhibitglue.tex +\font\cmr=cmr10\cmr +\font\tentt=cmtt10\tentt +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi + \parindent0pt \def\head#1{\par\noindent{\tentt ■\string#1}\par} \noautospacing \noautoxspacing @@ -82,13 +92,13 @@ あ)\adj\inhibitglue\adj (あ\par % 全角【TL17二分】 あ)\adj (あ\par % 全角 -\head{\math} -\def\math{$ $} -あ)\inhibitglue (あ\par % ベタ -あ)\inhibitglue\math (あ\par % 二分 -あ)\math\inhibitglue (あ\par % 二分 -あ)\math\inhibitglue\math (あ\par % 全角【TL17二分】 -あ)\math (あ\par % 全角 +%\head{\math} +%\def\math{$ $} +%あ)\inhibitglue (あ\par % ベタ +%あ)\inhibitglue\math (あ\par % 二分 +%あ)\math\inhibitglue (あ\par % 二分 +%あ)\math\inhibitglue\math (あ\par % 全角【TL17二分】 +%あ)\math (あ\par % 全角 %%% unexpandable tokens with discardable nodes (glue, kern, penalty) diff --git a/Build/source/texk/web2c/euptexdir/tests/jctrlsym.log b/Build/source/texk/web2c/euptexdir/tests/jctrlsym.log new file mode 100644 index 00000000000..c919554f580 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/jctrlsym.log @@ -0,0 +1,85 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-jctrlsym.tex +(./eup-jctrlsym.tex +■control symbol +> \X=macro: +->\+. +l.27 \show\X + \relax\message{(\meaning\X)} + +(macro:->\+) +> \Y=macro: +->\+. +l.28 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\+) ■control word +> \X=macro: +->\+ . +l.39 \show\X + \relax\message{(\meaning\X)} + +(macro:->\+ ) +> \Y=macro: +->\+ . +l.40 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\+ ) ■control word +> \X=macro: +->\ABC . +l.48 \show\X + \relax\message{(\meaning\X)} + +(macro:->\ABC ) +> \Y=macro: +->\あいう . +l.49 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\あいう ) ■csname +> \X=macro: +->\:AB . +l.62 \show\X + \relax\message{(\meaning\X)} + +(macro:->\:AB ) +> \Y=macro: +->\!あい . +l.63 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\!あい ) +> \X=macro: +->\AB: . +l.69 \show\X + \relax\message{(\meaning\X)} + +(macro:->\AB: ) +> \Y=macro: +->\あい! . +l.70 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\あい! ) ■not_cjk +> \X=macro: +->\^^e3^^82^^a2 . +l.81 \show\X + \relax\message{(\meaning\X)} + +(macro:->\^^e3^^82^^a2 ) +> \Y=macro: +->\^^ef^^bc^^81 . +l.82 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\^^ef^^bc^^81 ) ■行末の和文^^e3^^82^^b3^^e3^^83^^b3^^e3^^83^^88 +^^e3^^83^^ad^^e3^^83^^bc^^e3^^83^^ab^^e3^^82^^b7^^e3^^83^^b3^^e3^^83^^9c^^e3^^8 +3^^ab +Missing character: There is no ^^ef in font cmr10! +Missing character: There is no ^^bc in font cmr10! +Missing character: There is no ^^8c in font cmr10! + [1] ) +Output written on eup-jctrlsym.dvi (1 page, 372 bytes). diff --git a/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log new file mode 100644 index 00000000000..be8dbe27583 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.log @@ -0,0 +1,84 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-kinsoku_search.tex +(./eup-kinsoku_search.tex{into \tracingassigns=1} +{changing kinsoku table 1, type=no, code=0} +{into kinsoku table 1, type=pre, code=12289} +{changing kinsoku table 1, penalty=0} +{into kinsoku table 1, penalty=10000} +{changing kinsoku table 2, type=no, code=0} +{into kinsoku table 2, type=pre, code=12290} +{changing kinsoku table 2, penalty=0} +{into kinsoku table 2, penalty=10000} +{changing kinsoku table 3, type=no, code=0} +{into kinsoku table 3, type=pre, code=12353} +{changing kinsoku table 3, penalty=0} +{into kinsoku table 3, penalty=150} +{changing kinsoku table 4, type=no, code=0} +{into kinsoku table 4, type=pre, code=12355} +{changing kinsoku table 4, penalty=0} +{into kinsoku table 4, penalty=150} +> 0. +l.11 \showthe\currentgrouplevel + % > 0. + +> 150. +l.12 \showthe\prebreakpenalty`ぃ + % > 150. + +{changing kinsoku table 4, type=pre, code=12355} +{into kinsoku table 4, type=unused, code=0} +> 0. +l.15 \showthe\prebreakpenalty`ぃ + % > 0. ← OK(hash=4読出) + +{changing kinsoku table 4, type=unused, code=0} +{into kinsoku table 4, type=pre, code=12355} +{changing kinsoku table 4, penalty=150} +{into kinsoku table 4, penalty=123} +> 123. +l.18 \showthe\prebreakpenalty`ぃ + % > 123. ← OK(hash=4読出) + +{changing kinsoku table 3, type=pre, code=12353} +{into kinsoku table 3, type=unused, code=0} +> 123. +l.21 \showthe\prebreakpenalty`ぃ + % > 123. ← OK(hash=4読出) + +{changing kinsoku table 4, type=pre, code=12355} +{into kinsoku table 4, type=unused, code=0} +> 0. +l.27 \showthe\prebreakpenalty`ぃ + % > 0. ← 修正(削除済み) + +{changing kinsoku table 3, type=unused, code=0} +{into kinsoku table 3, type=pre, code=12355} +{changing kinsoku table 3, penalty=150} +{into kinsoku table 3, penalty=234} +> 234. +l.30 \showthe\prebreakpenalty`ぃ + % > 234. ← OK(hash=3読出) + +{changing kinsoku table 3, type=pre, code=12355} +{into kinsoku table 3, type=unused, code=0} +> 0. +l.33 \showthe\prebreakpenalty`ぃ + % > 0. ← 修正(削除済み) + +{changing kinsoku table 3, type=unused, code=0} +{into kinsoku table 3, type=pre, code=12353} +{changing kinsoku table 3, penalty=234} +{into kinsoku table 3, penalty=345} +> 0. +l.36 \showthe\prebreakpenalty`ぃ + % > 0. ← 修正(削除済み) + +{reassigning kinsoku table 4, type=unused, code=0} +> 0. +l.41 \showthe\prebreakpenalty`ぃ + % > 0. ← OK(削除済み) + + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/kinsoku_search.tex b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.tex index e59445e7d9c..e59445e7d9c 100644 --- a/Build/source/texk/web2c/ptexdir/tests/kinsoku_search.tex +++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_search.tex diff --git a/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log new file mode 100644 index 00000000000..46968151697 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.log @@ -0,0 +1,38 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-kinsoku_table.tex +(./eup-kinsoku_table.tex +\fuga=\count26 +\dflt=\count27 + +\hoge=\count28 +12288 12291 12294 12297 12300 12303 12306 12309 12312 12315 12318 12321 12324 +12327 12330 12333 12336 12339 12342 12345 12348 12351 12354 12357 12360 12363 +12366 12369 12372 12375 12378 12381 12384 12387 12390 12393 12396 12399 12402 +12405 12408 12411 12414 12417 12420 12423 12426 12429 12432 12435 12438 12441 +12444 12447 12450 12453 12456 12459 12462 12465 12468 12471 12474 12477 12480 +12483 12486 12489 12492 12495 12498 12501 12504 12507 12510 12513 12516 12519 +12522 12525 12528 12531 12534 12537 12540 12543 12546 12549 12552 12555 12558 +12561 12564 12567 12570 12573 12576 12579 12582 12585 12588 12591 12594 12597 +12600 12603 12606 12609 12612 12615 12618 12621 12624 12627 12630 12633 12636 +12639 12642 12645 12648 12651 12654 12657 12660 12663 12666 12669 12672 12675 +12678 12681 12684 12687 12690 12693 12696 12699 12702 12705 12708 12711 12714 +12717 12720 12723 12726 12729 12732 12735 12738 12741 12744 12747 12750 12753 +12756 12759 12762 12765 12768 12771 12774 12777 12780 12783 12786 12789 12792 +12795 12798 12801 12804 12807 12810 12813 12816 12819 12822 12825 12828 12831 +12834 12837 12840 12843 12846 12849 12852 12855 12858 12861 12864 12867 12870 +12873 12876 12879 12882 12885 12888 12891 12894 12897 12900 12903 12906 12909 +12912 12915 12918 12921 12924 12927 12930 12933 12936 12939 12942 12945 12948 +12951 12954 12957 12960 12963 12966 12969 12972 12975 12978 12981 12984 12987 +12990 12993 12996 12999 13002 13005 13008 13011 13014 13017 13020 13023 13026 +13029 13032 13035 13038 13041 13044 13047 13050 13053 <2, 0> +! KINSOKU table is full!!. +<to be read again> + \relax +l.23 \global\prebreakpenalty"4000=1\relax + % error +I'm skipping this control sequences. + +<0, 0> <2, 0> <0, 1> <0, 0> <0, 1, 0> <0, 0, 1> ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/kinsoku_table.tex b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.tex index 52b21fdbf86..a064af9fc41 100644 --- a/Build/source/texk/web2c/ptexdir/tests/kinsoku_table.tex +++ b/Build/source/texk/web2c/euptexdir/tests/kinsoku_table.tex @@ -1,6 +1,7 @@ %#!uptex -ini -\let\dump\relax -\input plain +%\let\dump\relax +%\input plain + \newcount\fuga\newcount\dflt \fuga="3300 \iffalse diff --git a/Build/source/texk/web2c/euptexdir/tests/pknj-e.log b/Build/source/texk/web2c/euptexdir/tests/pknj-e.log new file mode 100644 index 00000000000..a38e4fec2ff --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/pknj-e.log @@ -0,0 +1,224 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**printkanji +(./printkanji.tex +\openout1 = `eup-pknj-e.out'. + + +{MSG ^^c5^^bf ^^c5^^bf 顛 } +{TOT ^^c5^^bf ^^c5^^bf 顛 } +> \A=macro: +->^^c5^^bf ^^c5^^bf 顛 . +l.29 \show\A + + +■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +> \B=macro: +->macro:->^^c5^^bf ^^c5^^bf 顛 . +l.40 \show\B + + +■\jobname. {MSG *あ*eup-pknj-e*:eup-pknj-e:*macro:->:eup-pknj-e:*} +{TOT *あ*eup-pknj-e*:eup-pknj-e:*macro:->:eup-pknj-e:*} + +■oneletter. {MSG \catcode`\^^c5= 197} +{TOT \catcode`\^^c5= 197} +{MSG \catcode`^^c5 = 197} +{TOT \catcode`^^c5 = 197} +{MSG \catcode`^^c5^^bf = 197} +{TOT \catcode`^^c5^^bf = 197} +{MSG \catcode`顛 = 50623} +{TOT \catcode`顛 = 50623} +■csname1. +> \顛=macro: +->hoge. +l.72 \show\顛 + + +> \^^c5^^bf=macro: +->piyo. +l.73 \show\^^c5^^bf + + +> \^^c5^^bf=macro: +->piyo. +<recently read> \^^c5^^bf + +l.74 \expandafter\show\csname ^^c5^^bf\endcsname + + +{MSG \顛=>macro:->hoge} +{TOT \顛=>macro:->hoge} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} +{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} + +{MSG \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. } +{TOT \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. } +■0xFF. +{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +■contexts. +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1 + \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ... +l.136 \+\a + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a + \^^ff ^^ff\^^c5^^bf +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5 + \^^ff\^^c4\^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4 + \^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4\^^fe + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +\^^c5^^bf\^^ff^^c5 +! Undefined control sequence. +\b ->\^^c5^^be + .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd + ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 + . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be + .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd + ..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼 + . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +■csname2. +> \^^c5^^bf=macro: +->a. +l.162 SHOW \show\^^c5^^bf + .\show\顛.% + +> \顛=macro: +->b. +l.162 SHOW \show\^^c5^^bf.\show\顛 + .% + +> \^^c5^^bf=macro: +->a. +<recently read> \^^c5^^bf + +l.163 \expandafter\show\csname ^^c5^^bf\endcsname + .% + +> \顛=macro: +->b. +<recently read> \顛 + +l.164 \expandafter\show\csname 顛\endcsname + + +\^^e3^^81^^82\あ [50623] +Memory usage before: 1790&5515; after: 130&5080; still untouched: 4992452 + ) +Here is how much of TeX's memory you used: + 25 strings out of 497XXX + 143 string characters out of 6215XXX + 7548 words of memory out of 5000000 + 1090 multiletter control sequences out of 15000+600000 + 3271 words of font info for 2 fonts, out of 8000000 for 9000 + 0 hyphenation exceptions out of 8191 + 6i,4n,2p,97b,26s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on eup-pknj-e.dvi (1 page, 1140 bytes). diff --git a/Build/source/texk/web2c/euptexdir/tests/pknj-s.log b/Build/source/texk/web2c/euptexdir/tests/pknj-s.log new file mode 100644 index 00000000000..4fea4e53a4a --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/pknj-s.log @@ -0,0 +1,224 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**printkanji +(./printkanji.tex +\openout1 = `eup-pknj-s.out'. + + +{MSG ^^c5^^bf ^^c5^^bf 顛 } +{TOT ^^c5^^bf ^^c5^^bf 顛 } +> \A=macro: +->^^c5^^bf ^^c5^^bf 顛 . +l.29 \show\A + + +■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +> \B=macro: +->macro:->^^c5^^bf ^^c5^^bf 顛 . +l.40 \show\B + + +■\jobname. {MSG *あ*eup-pknj-s*:eup-pknj-s:*macro:->:eup-pknj-s:*} +{TOT *あ*eup-pknj-s*:eup-pknj-s:*macro:->:eup-pknj-s:*} + +■oneletter. {MSG \catcode`\^^c5= 197} +{TOT \catcode`\^^c5= 197} +{MSG \catcode`^^c5 = 197} +{TOT \catcode`^^c5 = 197} +{MSG \catcode`^^c5^^bf = 197} +{TOT \catcode`^^c5^^bf = 197} +{MSG \catcode`顛 = 37726} +{TOT \catcode`顛 = 37726} +■csname1. +> \顛=macro: +->hoge. +l.72 \show\顛 + + +> \^^c5^^bf=macro: +->piyo. +l.73 \show\^^c5^^bf + + +> \^^c5^^bf=macro: +->piyo. +<recently read> \^^c5^^bf + +l.74 \expandafter\show\csname ^^c5^^bf\endcsname + + +{MSG \顛=>macro:->hoge} +{TOT \顛=>macro:->hoge} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} +{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} + +{MSG \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. } +{TOT \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. } +■0xFF. +{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +■contexts. +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1 + \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ... +l.136 \+\a + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a + \^^ff ^^ff\^^c5^^bf +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5 + \^^ff\^^c4\^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4 + \^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4\^^fe + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +\^^c5^^bf\^^ff^^c5 +! Undefined control sequence. +\b ->\^^c5^^be + .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd + ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 + . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be + .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd + ..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼 + . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +■csname2. +> \^^c5^^bf=macro: +->a. +l.162 SHOW \show\^^c5^^bf + .\show\顛.% + +> \顛=macro: +->b. +l.162 SHOW \show\^^c5^^bf.\show\顛 + .% + +> \^^c5^^bf=macro: +->a. +<recently read> \^^c5^^bf + +l.163 \expandafter\show\csname ^^c5^^bf\endcsname + .% + +> \顛=macro: +->b. +<recently read> \顛 + +l.164 \expandafter\show\csname 顛\endcsname + + +\^^e3^^81^^82\あ [37726] +Memory usage before: 1798&5518; after: 130&5081; still untouched: 4992449 + ) +Here is how much of TeX's memory you used: + 24 strings out of 497XXX + 141 string characters out of 6215XXX + 7551 words of memory out of 5000000 + 1089 multiletter control sequences out of 15000+600000 + 3271 words of font info for 2 fonts, out of 8000000 for 9000 + 0 hyphenation exceptions out of 8191 + 6i,4n,2p,97b,26s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on eup-pknj-s.dvi (1 page, 1148 bytes). diff --git a/Build/source/texk/web2c/euptexdir/tests/pknj-u.log b/Build/source/texk/web2c/euptexdir/tests/pknj-u.log new file mode 100644 index 00000000000..f456fc4c18e --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/pknj-u.log @@ -0,0 +1,224 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**printkanji +(./printkanji.tex +\openout1 = `eup-pknj-u.out'. + + +{MSG ^^c5^^bf ^^c5^^bf 顛 } +{TOT ^^c5^^bf ^^c5^^bf 顛 } +> \A=macro: +->^^c5^^bf ^^c5^^bf 顛 . +l.29 \show\A + + +■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +> \B=macro: +->macro:->^^c5^^bf ^^c5^^bf 顛 . +l.40 \show\B + + +■\jobname. {MSG *あ*eup-pknj-u*:eup-pknj-u:*macro:->:eup-pknj-u:*} +{TOT *あ*eup-pknj-u*:eup-pknj-u:*macro:->:eup-pknj-u:*} + +■oneletter. {MSG \catcode`\^^c5= 197} +{TOT \catcode`\^^c5= 197} +{MSG \catcode`^^c5 = 197} +{TOT \catcode`^^c5 = 197} +{MSG \catcode`^^c5^^bf = 197} +{TOT \catcode`^^c5^^bf = 197} +{MSG \catcode`顛 = 39003} +{TOT \catcode`顛 = 39003} +■csname1. +> \顛=macro: +->hoge. +l.72 \show\顛 + + +> \^^c5^^bf=macro: +->piyo. +l.73 \show\^^c5^^bf + + +> \^^c5^^bf=macro: +->piyo. +<recently read> \^^c5^^bf + +l.74 \expandafter\show\csname ^^c5^^bf\endcsname + + +{MSG \顛=>macro:->hoge} +{TOT \顛=>macro:->hoge} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} +{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} + +{MSG \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. } +{TOT \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. } +■0xFF. +{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf +:macro:->あ).} +{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf +:macro:->あ).} +{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +■contexts. +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1 + \^^ff macro:->^^c5^^bf ^^c5^^bf ... +l.136 \+\a + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +l.138 ...^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a + \^^ff ^^ff\^^c5^^bf +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5 + \^^ff\^^c4\^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4 + \^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4\^^fe + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +\^^c5^^bf\^^ff^^c5 +! Undefined control sequence. +\b ->\^^c5^^be + .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd + ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ...be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 + . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be + .\ſ.\Ž..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd + ..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 ...5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼 + . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +■csname2. +> \^^c5^^bf=macro: +->a. +l.162 SHOW \show\^^c5^^bf + .\show\顛.% + +> \顛=macro: +->b. +l.162 SHOW \show\^^c5^^bf.\show\顛 + .% + +> \^^c5^^bf=macro: +->a. +<recently read> \^^c5^^bf + +l.163 \expandafter\show\csname ^^c5^^bf\endcsname + .% + +> \顛=macro: +->b. +<recently read> \顛 + +l.164 \expandafter\show\csname 顛\endcsname + + +\^^e3^^81^^82\あ [39003] +Memory usage before: 1798&5518; after: 130&5081; still untouched: 4992449 + ) +Here is how much of TeX's memory you used: + 24 strings out of 497XXX + 146 string characters out of 6215XXX + 7551 words of memory out of 5000000 + 1089 multiletter control sequences out of 15000+600000 + 3297 words of font info for 2 fonts, out of 8000000 for 9000 + 0 hyphenation exceptions out of 8191 + 6i,4n,2p,97b,26s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on eup-pknj-u.dvi (1 page, 1148 bytes). diff --git a/Build/source/texk/web2c/euptexdir/tests/postpena.log b/Build/source/texk/web2c/euptexdir/tests/postpena.log new file mode 100644 index 00000000000..6924c21fe99 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/postpena.log @@ -0,0 +1,43 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-postpena.tex +(./eup-postpena.tex + +### yoko direction, restricted horizontal mode entered at line 15 +\displace 0.0 +\jpy # +\penalty 200(for kinsoku) +\jpy 」 +\glue(refer from jfm) 4.58203 minus 2.291 +\jpy 「 +\penalty -1000 +spacefactor 1000 +### yoko direction, vertical mode entered at line 0 +prevdepth ignored + +! OK. +<recently read> \showlists + +l.15 ...ox0=\hbox{#」「\penalty-1000\showlists + } + + +### yoko direction, restricted horizontal mode entered at line 18 +\displace 0.0 +\jpy # +\jpy 」 +\glue(refer from jfm) 4.58203 minus 2.291 +\cmr a +\penalty 200(for kinsoku) +\jpy あ +spacefactor 1000 +### yoko direction, vertical mode entered at line 0 +prevdepth ignored + +! OK. +l.18 ...box{#\unpenalty 」a\relax あ\showlists + } + + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/postpena.tex b/Build/source/texk/web2c/euptexdir/tests/postpena.tex index 74f93a1f071..3605fe0c0b3 100644 --- a/Build/source/texk/web2c/ptexdir/tests/postpena.tex +++ b/Build/source/texk/web2c/euptexdir/tests/postpena.tex @@ -1,4 +1,12 @@ %% plain pTeX +\font\cmr=cmr10\cmr +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi \postbreakpenalty`#=200 \postbreakpenalty`a=200 \showboxdepth10000 diff --git a/Build/source/texk/web2c/euptexdir/tests/printkanji.tex b/Build/source/texk/web2c/euptexdir/tests/printkanji.tex new file mode 100644 index 00000000000..d176d9a53bf --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/printkanji.tex @@ -0,0 +1,176 @@ +%\scrollmode +\tracingstats=1000 +\noautoxspacing +\newlinechar=`\^^J +\font\x=ec-lmr10 \x +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi + + +\immediate\openout1=\jobname.out +\def\MSG#1{% + \message{\string{MSG #1\string}}% + \immediate\write17{\string{TOT #1\string}}% + \immediate\write1{#1}% +} +\def\head#1{\message{■#1.}\par\noindent\hbox{■\null}#1.\par} +\message{^^J} + +%================ +\def\A{^^c5^^bf ſ 顛 } +A: \A + +\MSG{\A} +\show\A + +%================ +\par +\head{\string\meaning} + +\edef\B{\meaning\A} +\meaning\B + +\B +\MSG{\meaning\B} +\show\B + +%================ +\head{\string\jobname} +\edef\C{:\jobname:} +\MSG{*あ*\jobname *\C*\meaning\C*} +\message{^^J} + +%================ +\catcode`\^^c5=11 +\catcode`\^^bf=11 +\catcode`\^^e1=11 +\catcode`\^^e3=11 +\catcode`\^^81=11 +\catcode`\^^82=11 + +\head{oneletter} + +\string\^^c4.\string\^^c5.\string\^^ff. +\count0=`\^^c5% +\MSG{\string\catcode`\string\^^c5 = \the\count0}% +\count0=`^^c5% +\MSG{\string\catcode`^^c5 = \the\count0}% +\count0=`^^c5^^bf% +\MSG{\string\catcode`^^c5^^bf = \the\count0}% +\count0=`顛% +\MSG{\string\catcode`顛 = \the\count0}% + +%================ +\head{csname1} + +{\def\顛{hoge}\def\^^c5^^bf{piyo} +\show\顛 +\show\^^c5^^bf +\expandafter\show\csname ^^c5^^bf\endcsname +\MSG{\string\顛=>\meaning\顛} +\MSG{\string\^^c5^^bf=>\meaning\^^c5^^bf} +\MSG{\expandafter\string\csname ^^c5^^bf\endcsname + =>\expandafter\meaning\csname ^^c5^^bf\endcsname}} + +\string\^^c5^^bf +\expandafter\string\csname ^^c5^^bf\endcsname, +\expandafter\string\csname ſ\endcsname, +\expandafter\string\csname 顛\endcsname +\MSG{\string\^^c5^^bf, + \expandafter\string\csname ^^c5^^bf\endcsname, + \expandafter\string\csname ſ\endcsname, + \expandafter\string\csname 顛\endcsname.} + +%================ + +\def\あ{hoge} +\message{^^J} +\def\TEST#1#2{% + \expandafter\def\csname#2\endcsname{piyo} + \par\toks0={#2} + \expandafter\string\csname #1\endcsname => \csname #1\endcsname,\par + \expandafter\string\csname #2\endcsname => \csname #2\endcsname,\par + \expandafter\string\csname \the\toks0\endcsname => \csname \the\toks0\endcsname. + \MSG{% + \expandafter\string\csname #1\endcsname => \csname #1\endcsname, + \expandafter\string\csname #2\endcsname => \csname #2\endcsname. + \expandafter\string\csname \the\toks0\endcsname => \csname \the\toks0\endcsname. + }% +} +\ifnum\euc"A4A2="A4A2\relax + \TEST{あ}{^^a4^^a2} +\else + \TEST{あ}{^^e3^^81^^82} +\fi + +%================ +\head{0xFF} +\catcode"FF=11 +\message{^^J} + +\def\^^ff^^c5^^ff^^bf{あ} +\edef\E{(\string\^^ff^^c5^^ff^^bf:\meaning\^^ff^^c5^^ff^^bf)} +\^^ff^^c5^^ff^^bf, \E, \string\^^ff^^c5^^ff^^bf, \meaning\E. +\MSG{\^^ff^^c5^^ff^^bf, \A, \string\^^ff^^c5^^ff^^bf, \meaning\E.} + +\def\^^ff{い} +\edef\E{(\string\^^ff :\meaning\^^ff)} +\^^ff, \E, \string\^^ff, \meaning\E. +\MSG{\^^ff, \E, \string\^^ff, \meaning\E.} + +\immediate\closeout1 + +%======== +\head{contexts} + +\errorcontextlines=10000 +\def\@{\A\undefined} +\edef\a{^^c5^^bf a\A\noexpand\@ ſ 顛 } +\edef\+#1{\noexpand\^^c5^^bf\noexpand\顛\noexpand\^^ff^^c5^^ff^^bf#1 + \noexpand\^^ff\meaning\A\noexpand\^^c5^^bf\noexpand\顛} +\+\a + +\^^ff^^c5^^ff^^bf ^^c5^^bf \^^c5^^bf ſ顛\a \^^ff ^^ff\^^c5^^bf + +\^^c5\^^ff\^^c4\^^fe + +\catcode`\^^c5=11 +\catcode`\^^be=11 +\catcode`\^^bf=11 +\catcode`\^^bd=11 +\catcode`\^^ff=11 + +\def\^^c5^^bf{a} +\def\顛{b} +\def\転{c} +\message{\string\^^c5^^bf \string\^^ff^^c5} + +\def\b{\ž.\ſ.\Ž..\転.\顛.\貼.} +\b + +\ž.\ſ.\Ž..\転.\顛.\貼. + + +%======== +\head{csname2} + +SHOW \show\ſ.\show\顛.% +\expandafter\show\csname ſ\endcsname.% +\expandafter\show\csname 顛\endcsname + +STRING \string\ſ.\string\顛.% +\expandafter\string\csname ſ\endcsname.% +\expandafter\string\csname 顛\endcsname + +MEANING \meaning\ſ.\meaning\顛.% +\expandafter\meaning\csname ſ\endcsname.% +\expandafter\meaning\csname 顛\endcsname.% + +\message{\string\^^e3^^81^^82 \string\あ} + +\end diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs-e.log b/Build/source/texk/web2c/euptexdir/tests/ucs-e.log new file mode 100644 index 00000000000..684d2af0fa6 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/ucs-e.log @@ -0,0 +1,29 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-ucs +(./eup-ucs.tex +<EUC> +\ucs. +\cntA=\count26 +162: 41457(¢) +163: 41458(£) +165: 41455(¥) +167: 41464(§) +168: 41391(¨) +172: 41676(¬) +176: 41451(°) +177: 41438(±) +180: 41389(´) +182: 41721(¶) +215: 41439(×) +247: 41440(÷) +\toucs. +-1, -1, -1, -1, -1 +-1, 34821, 34722 +12354, 29081, -1 +41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676 +41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676 +41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676 + ) +No pages of output. diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs-s.log b/Build/source/texk/web2c/euptexdir/tests/ucs-s.log new file mode 100644 index 00000000000..a8255446072 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/ucs-s.log @@ -0,0 +1,29 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-ucs +(./eup-ucs.tex +<SJIS> +\ucs. +\cntA=\count26 +162: 33169(¢) +163: 33170(£) +165: 33167(¥) +167: 33176(§) +168: 33102(¨) +172: 33226(¬) +176: 33163(°) +177: 33149(±) +180: 33100(´) +182: 33271(¶) +215: 33150(×) +247: 33152(÷) +\toucs. +-1, -1, -1, -1, -1 +12450, 29081, -1 +-1, -1, -1 +33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226 +33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226 +33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226 + ) +No pages of output. diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs-u.log b/Build/source/texk/web2c/euptexdir/tests/ucs-u.log new file mode 100644 index 00000000000..c4dfe33b080 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/ucs-u.log @@ -0,0 +1,529 @@ +entering extended mode + restricted \write18 enabled. + %&-line parsing enabled. +**eup-ucs +(./eup-ucs.tex +<uptex> +\ucs. +\cntA=\count26 +0: 0(^^@) +1: 1(^^A) +2: 2(^^B) +3: 3(^^C) +4: 4(^^D) +5: 5(^^E) +6: 6(^^F) +7: 7(^^G) +8: 8(^^H) +9: 9(^^I) +10: 10(^^J) +11: 11(^^K) +12: 12(^^L) +13: 13(^^M) +14: 14(^^N) +15: 15(^^O) +16: 16(^^P) +17: 17(^^Q) +18: 18(^^R) +19: 19(^^S) +20: 20(^^T) +21: 21(^^U) +22: 22(^^V) +23: 23(^^W) +24: 24(^^X) +25: 25(^^Y) +26: 26(^^Z) +27: 27(^^[) +28: 28(^^\) +29: 29(^^]) +30: 30(^^^) +31: 31(^^_) +32: 32( ) +33: 33(!) +34: 34(") +35: 35(#) +36: 36($) +37: 37(%) +38: 38(&) +39: 39(') +40: 40(() +41: 41()) +42: 42(*) +43: 43(+) +44: 44(,) +45: 45(-) +46: 46(.) +47: 47(/) +48: 48(0) +49: 49(1) +50: 50(2) +51: 51(3) +52: 52(4) +53: 53(5) +54: 54(6) +55: 55(7) +56: 56(8) +57: 57(9) +58: 58(:) +59: 59(;) +60: 60(<) +61: 61(=) +62: 62(>) +63: 63(?) +64: 64(@) +65: 65(A) +66: 66(B) +67: 67(C) +68: 68(D) +69: 69(E) +70: 70(F) +71: 71(G) +72: 72(H) +73: 73(I) +74: 74(J) +75: 75(K) +76: 76(L) +77: 77(M) +78: 78(N) +79: 79(O) +80: 80(P) +81: 81(Q) +82: 82(R) +83: 83(S) +84: 84(T) +85: 85(U) +86: 86(V) +87: 87(W) +88: 88(X) +89: 89(Y) +90: 90(Z) +91: 91([) +92: 92(\) +93: 93(]) +94: 94(^) +95: 95(_) +96: 96(`) +97: 97(a) +98: 98(b) +99: 99(c) +100: 100(d) +101: 101(e) +102: 102(f) +103: 103(g) +104: 104(h) +105: 105(i) +106: 106(j) +107: 107(k) +108: 108(l) +109: 109(m) +110: 110(n) +111: 111(o) +112: 112(p) +113: 113(q) +114: 114(r) +115: 115(s) +116: 116(t) +117: 117(u) +118: 118(v) +119: 119(w) +120: 120(x) +121: 121(y) +122: 122(z) +123: 123({) +124: 124(|) +125: 125(}) +126: 126(~) +127: 127(^^?) +128: 128(^^80) +129: 129(^^81) +130: 130(^^82) +131: 131(^^83) +132: 132(^^84) +133: 133(^^85) +134: 134(^^86) +135: 135(^^87) +136: 136(^^88) +137: 137(^^89) +138: 138(^^8a) +139: 139(^^8b) +140: 140(^^8c) +141: 141(^^8d) +142: 142(^^8e) +143: 143(^^8f) +144: 144(^^90) +145: 145(^^91) +146: 146(^^92) +147: 147(^^93) +148: 148(^^94) +149: 149(^^95) +150: 150(^^96) +151: 151(^^97) +152: 152(^^98) +153: 153(^^99) +154: 154(^^9a) +155: 155(^^9b) +156: 156(^^9c) +157: 157(^^9d) +158: 158(^^9e) +159: 159(^^9f) +160: 160(^^a0) +161: 161(^^a1) +162: 162(^^a2) +163: 163(^^a3) +164: 164(^^a4) +165: 165(^^a5) +166: 166(^^a6) +167: 167(^^a7) +168: 168(^^a8) +169: 169(^^a9) +170: 170(^^aa) +171: 171(^^ab) +172: 172(^^ac) +173: 173(^^ad) +174: 174(^^ae) +175: 175(^^af) +176: 176(^^b0) +177: 177(^^b1) +178: 178(^^b2) +179: 179(^^b3) +180: 180(^^b4) +181: 181(^^b5) +182: 182(^^b6) +183: 183(^^b7) +184: 184(^^b8) +185: 185(^^b9) +186: 186(^^ba) +187: 187(^^bb) +188: 188(^^bc) +189: 189(^^bd) +190: 190(^^be) +191: 191(^^bf) +192: 192(^^c0) +193: 193(^^c1) +194: 194(^^c2) +195: 195(^^c3) +196: 196(^^c4) +197: 197(^^c5) +198: 198(^^c6) +199: 199(^^c7) +200: 200(^^c8) +201: 201(^^c9) +202: 202(^^ca) +203: 203(^^cb) +204: 204(^^cc) +205: 205(^^cd) +206: 206(^^ce) +207: 207(^^cf) +208: 208(^^d0) +209: 209(^^d1) +210: 210(^^d2) +211: 211(^^d3) +212: 212(^^d4) +213: 213(^^d5) +214: 214(^^d6) +215: 215(^^d7) +216: 216(^^d8) +217: 217(^^d9) +218: 218(^^da) +219: 219(^^db) +220: 220(^^dc) +221: 221(^^dd) +222: 222(^^de) +223: 223(^^df) +224: 224(^^e0) +225: 225(^^e1) +226: 226(^^e2) +227: 227(^^e3) +228: 228(^^e4) +229: 229(^^e5) +230: 230(^^e6) +231: 231(^^e7) +232: 232(^^e8) +233: 233(^^e9) +234: 234(^^ea) +235: 235(^^eb) +236: 236(^^ec) +237: 237(^^ed) +238: 238(^^ee) +239: 239(^^ef) +240: 240(^^f0) +241: 241(^^f1) +242: 242(^^f2) +243: 243(^^f3) +244: 244(^^f4) +245: 245(^^f5) +246: 246(^^f6) +247: 247(^^f7) +248: 248(^^f8) +249: 249(^^f9) +250: 250(^^fa) +251: 251(^^fb) +252: 252(^^fc) +253: 253(^^fd) +254: 254(^^fe) +255: 255(^^ff) +\toucs. +0: 0 +1: 1 +2: 2 +3: 3 +4: 4 +5: 5 +6: 6 +7: 7 +8: 8 +9: 9 +10: 10 +11: 11 +12: 12 +13: 13 +14: 14 +15: 15 +16: 16 +17: 17 +18: 18 +19: 19 +20: 20 +21: 21 +22: 22 +23: 23 +24: 24 +25: 25 +26: 26 +27: 27 +28: 28 +29: 29 +30: 30 +31: 31 +32: 32 +33: 33 +34: 34 +35: 35 +36: 36 +37: 37 +38: 38 +39: 39 +40: 40 +41: 41 +42: 42 +43: 43 +44: 44 +45: 45 +46: 46 +47: 47 +48: 48 +49: 49 +50: 50 +51: 51 +52: 52 +53: 53 +54: 54 +55: 55 +56: 56 +57: 57 +58: 58 +59: 59 +60: 60 +61: 61 +62: 62 +63: 63 +64: 64 +65: 65 +66: 66 +67: 67 +68: 68 +69: 69 +70: 70 +71: 71 +72: 72 +73: 73 +74: 74 +75: 75 +76: 76 +77: 77 +78: 78 +79: 79 +80: 80 +81: 81 +82: 82 +83: 83 +84: 84 +85: 85 +86: 86 +87: 87 +88: 88 +89: 89 +90: 90 +91: 91 +92: 92 +93: 93 +94: 94 +95: 95 +96: 96 +97: 97 +98: 98 +99: 99 +100: 100 +101: 101 +102: 102 +103: 103 +104: 104 +105: 105 +106: 106 +107: 107 +108: 108 +109: 109 +110: 110 +111: 111 +112: 112 +113: 113 +114: 114 +115: 115 +116: 116 +117: 117 +118: 118 +119: 119 +120: 120 +121: 121 +122: 122 +123: 123 +124: 124 +125: 125 +126: 126 +127: 127 +128: 128 +129: 129 +130: 130 +131: 131 +132: 132 +133: 133 +134: 134 +135: 135 +136: 136 +137: 137 +138: 138 +139: 139 +140: 140 +141: 141 +142: 142 +143: 143 +144: 144 +145: 145 +146: 146 +147: 147 +148: 148 +149: 149 +150: 150 +151: 151 +152: 152 +153: 153 +154: 154 +155: 155 +156: 156 +157: 157 +158: 158 +159: 159 +160: 160 +161: 161 +162: 162 +163: 163 +164: 164 +165: 165 +166: 166 +167: 167 +168: 168 +169: 169 +170: 170 +171: 171 +172: 172 +173: 173 +174: 174 +175: 175 +176: 176 +177: 177 +178: 178 +179: 179 +180: 180 +181: 181 +182: 182 +183: 183 +184: 184 +185: 185 +186: 186 +187: 187 +188: 188 +189: 189 +190: 190 +191: 191 +192: 192 +193: 193 +194: 194 +195: 195 +196: 196 +197: 197 +198: 198 +199: 199 +200: 200 +201: 201 +202: 202 +203: 203 +204: 204 +205: 205 +206: 206 +207: 207 +208: 208 +209: 209 +210: 210 +211: 211 +212: 212 +213: 213 +214: 214 +215: 215 +216: 216 +217: 217 +218: 218 +219: 219 +220: 220 +221: 221 +222: 222 +223: 223 +224: 224 +225: 225 +226: 226 +227: 227 +228: 228 +229: 229 +230: 230 +231: 231 +232: 232 +233: 233 +234: 234 +235: 235 +236: 236 +237: 237 +238: 238 +239: 239 +240: 240 +241: 241 +242: 242 +243: 243 +244: 244 +245: 245 +246: 246 +247: 247 +248: 248 +249: 249 +250: 250 +251: 251 +252: 252 +253: 253 +254: 254 +255: 255 +0, 128, 215, 167, 65536 +33601, 60068, 60069 +42146, 62630, 62631 +65507, 8213, 12316, 8214, 8230, 8722, 65509, 65504, 65505, 65506 +8254, 8212, 12316, 8214, 8230, 8722, 165, 162, 163, 172 +65507, 8213, 65374, 8741, 8943, 65293, 65509, 65504, 65505, 65506 + ) +No pages of output. diff --git a/Build/source/texk/web2c/euptexdir/tests/ucs.tex b/Build/source/texk/web2c/euptexdir/tests/ucs.tex new file mode 100644 index 00000000000..520a7485d57 --- /dev/null +++ b/Build/source/texk/web2c/euptexdir/tests/ucs.tex @@ -0,0 +1,36 @@ +%#!eptex + +\def\typeout{\immediate\write17} + +\ifnum`あ="A4A2 \typeout{<EUC>} +\else\ifnum`ア="8341 \typeout{<SJIS>} +\else\typeout{<uptex>}\fi\fi + +\typeout{\string\ucs.} +\newcount\cntA +\loop\ifnum\cntA<256 + \ifnum0>\ucs\cntA\else\typeout{\the\cntA: \ucs\cntA % + \ifx\Uchar\undefined\else(\Uchar\ucs\cntA)\fi}\fi + \advance\cntA1 +\repeat + + +\typeout{\string\toucs.} +\cntA=0 +\loop\ifnum\cntA<256 + \ifnum0>\toucs\cntA\else\typeout{\the\cntA: \toucs\cntA}\fi + \advance\cntA1 +\repeat +\typeout{\toucs"0, \toucs"80, \toucs"D7, \toucs"A7, \toucs"10000} +\typeout{\toucs"8341, \toucs"EAA4, \toucs"EAA5} +\typeout{\toucs"A4A2, \toucs"F4A6, \toucs"F4A7} +\typeout{\jis"2131, \jis"213D, \jis"2141, \jis"2142, \jis"2144, + \jis"215D, \jis"216F, \jis"2171, \jis"2172, \jis"224C} +\typeout{\ucs"203E, \ucs"2014, \ucs"301C, \ucs"2016, \ucs"2026, + \ucs"2212, \ucs"00A5, \ucs"00A2, \ucs"00A3, \ucs"00AC} +\typeout{\ucs"FFE3, \ucs"2015, \ucs"FF5E, \ucs"2225, \ucs"22EF, + \ucs"FF0D, \ucs"FFE5, \ucs"FFE0, \ucs"FFE1, \ucs"FFE2} + + +\bye + diff --git a/Build/source/texk/web2c/gftodvi.ch b/Build/source/texk/web2c/gftodvi.ch index 90ecd85da2a..5b895f02ae0 100644 --- a/Build/source/texk/web2c/gftodvi.ch +++ b/Build/source/texk/web2c/gftodvi.ch @@ -35,6 +35,12 @@ % Revision 0.3 84/11/17 23:51:56 richards % Base version for GFtoDVI Version 0.3 +@x [0] l.29 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.32 \def\title{GF$\,$\lowercase{to}$\,$DVI} @y @@ -77,7 +83,7 @@ procedure initialize; {this procedure gets things started properly} const @<Constants in the outer block@>@/ type @<Types in the outer block@>@/ var @<Globals in the outer block@>@/ -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @!i,@!j,@!m,@!n:integer; {loop indices for initializations} begin @@ -612,7 +618,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 4; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/gftopk.ch b/Build/source/texk/web2c/gftopk.ch index 1afa9119ef7..f892e4ba309 100644 --- a/Build/source/texk/web2c/gftopk.ch +++ b/Build/source/texk/web2c/gftopk.ch @@ -11,6 +11,12 @@ % on the origin of fonts in both gf and pk formats. The program runs % silently unless it is given the -v switch in the command line. +@x [0] l.41 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.44 \def\title{GFtoPK} @y @@ -63,7 +69,7 @@ produced only when the \.{-v} command line flag is presented. const @<Constants in the outer block@>@/ type @<Types in the outer block@>@/ var @<Globals in the outer block@>@/ -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} begin @@ -82,7 +88,7 @@ begin @<Labels...@>=final_end; @y @ This module is deleted, because it is only useful for -a non-local goto, which we can't use in C. +a non-local |goto|\unskip, which we can't use in C. @z @x [6] Dynamic allocation of |row| array. @@ -414,7 +420,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) @d do_nothing == {empty statement} -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 3; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/gftype.ch b/Build/source/texk/web2c/gftype.ch index 49d7bf6b3e0..74bc8d4dec1 100644 --- a/Build/source/texk/web2c/gftype.ch +++ b/Build/source/texk/web2c/gftype.ch @@ -15,6 +15,12 @@ % turns on images. There is no terminal input to this program. % Output is to stdout, and may, of course, be redirected. +@x [0] l.27 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.30 \def\title{GFtype} @y @@ -57,7 +63,7 @@ procedure initialize; {this procedure gets things started properly} const @<Constants in the outer block@>@/ type @<Types in the outer block@>@/ var @<Globals in the outer block@>@/ -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} @!bound_default:integer; {temporary for setup} @@ -78,7 +84,7 @@ begin @<Labels...@>=final_end; @y @ This module is deleted, because it is only useful for -a non-local goto, which we can't use in C. +a non-local |goto|\unskip, which we can't use in C. Instead, we define parameters settable at runtime. @@ -443,7 +449,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) @d do_nothing == {empty statement} -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 4; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/hitexdir/hitex.w b/Build/source/texk/web2c/hitexdir/hitex.w index 7351c672e2d..cad0605c82f 100644 --- a/Build/source/texk/web2c/hitexdir/hitex.w +++ b/Build/source/texk/web2c/hitexdir/hitex.w @@ -224,7 +224,6 @@ @s alpha_file int @s byte_file int @s word_file int -@s int8_t int @s uint8_t int @s int16_t int @s uint16_t int @@ -250,13 +249,9 @@ @s line normal @s to do -\def\contentspagenumber{0} -\def\tocpages{3} % HiTeX has a long ToC -\pageno=\contentspagenumber \advance \pageno by \tocpages - @* Introduction. This is Hi\TeX, a program derived from \TeX, extending its capabilities -using \eTeX\ and \Prote, and adding functions common to other engines from +using \eTeX and \Prote, and adding functions common to other engines from the \TeX\ Live distribution. Hi\TeX\ writes output files in the \HINT\ file format. Like \TeX, it is a document compiler intended to produce typesetting of high @@ -380,7 +375,7 @@ known as `\Prote'. @d Prote_banner "This is Prote, Version " Prote_version_string /*printed when \Prote\ starts*/ @# -@d banner "This is HiTeX, Version 3.141592653"@| +@d banner "This is HiTeX, Version 3.141592653" eTeX_version_string"-"HINT_VERSION_STRING" "TL_VERSION /*printed when \TeX\ starts*/ @@ -436,7 +431,7 @@ portions of the program. The program starts with inserting header files and occassionaly a function must be placed before declaring \TeX's macros, because the function -uses identifiers that \TeX\ will declare as macros. +uses identifiers that \TeX will declare as macros. @p @<Header files and function declarations@>@; @h @@ -447,7 +442,7 @@ enum {@+@<Constants in the outer block@>@+}; @# static void initialize(void) /*this procedure gets things started properly*/ {@+@<Local variables for initialization@>@; - @<Initialize whatever \TeX\ might access@>@; + @<Initialize whatever \TeX\ might access@>; } @# @<Basic printing procedures@>@; @<Error handling procedures@>@; @@ -634,7 +629,7 @@ because some fussy \PASCAL\ compilers will complain about redundant labels. @d negate(A) A=-A /*change the sign of a variable*/ @d loop @+while (true) @+ /*repeat over and over until a |goto| happens*/ @f loop else - /*\.{WEB}'s |loop| acts like `\ignorespaces|while true do|\unskip'*/ + /*\.{WEB}'s |else| acts like `\ignorespaces|while true do|\unskip'*/ @d do_nothing /*empty statement*/ @d empty 0 /*symbolic name for a null constant*/ @@ -1334,7 +1329,7 @@ while (j < str_start[s+1]) result=true; not_found: return result; } -@#@<Declare \Prote\ procedures for strings@>@; +@t\4@>@<Declare \Prote\ procedures for strings@>@; @ The initial values of |str_pool|, |str_start|, |pool_ptr|, and |str_ptr| are computed by the \.{INITEX} program, based in part @@ -1425,8 +1420,6 @@ declaration. @<Forward declarations@>= static int s_no(const char *str); -@ (Empty section to keep numbering intact.) - @* 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 @@ -1498,10 +1491,8 @@ for terminal output, and it is possible to adhere to those conventions by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section. @^system dependencies@> -@s sizeof x - @<Basic printing procedures@>= -#define @[put(F)@] @[fwrite(&((F).d),sizeof((F).d),1,(F).f)@] +#define @[put(F)@] @[fwrite(&((F).d)@],@[sizeof((F).d),1,(F).f)@]@; #define @[get(F)@] @[fread(&((F).d),sizeof((F).d),1,(F).f)@] #define @[pascal_close(F)@] @[fclose((F).f)@] @@ -1708,8 +1699,8 @@ if (n < 0) } } } -do {dig[k]=n%10;n=n/10;incr(k); -} while (!(n==0)); +@/do@+{dig[k]=n%10;n=n/10;incr(k); +}@+ while (!(n==0)); print_the_digs(k); } @@ -1727,8 +1718,8 @@ print_char('0'+(n%10)); /*prints a positive integer in hexadecimal form*/ {@+int k; /*index to current digit; we assume that $0\le n<16^{22}$*/ k=0;print_char('"'); -do {dig[k]=n%16;n=n/16;incr(k); -} while (!(n==0)); +@/do@+{dig[k]=n%16;n=n/16;incr(k); +}@+ while (!(n==0)); print_the_digs(k); } @@ -1804,7 +1795,7 @@ $$\vbox{\halign{#\hfil\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}}$$ +|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 @@ -2010,6 +2001,7 @@ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if (deletions_allowed) @<Delete \(c)|c-"0"| tokens and |goto resume|@>@;@+break; +@t\4\4@>@; #ifdef @!DEBUG case 'D': {@+debug_help();goto resume;@+} #endif @@ -2102,8 +2094,8 @@ show_context();goto resume; else{@+if (help_ptr==0) help2("Sorry, I don't know how to help in this situation.",@/ @t\kern1em@>"Maybe you should try asking a human?"); - do {decr(help_ptr);print(help_line[help_ptr]);print_ln(); - } while (!(help_ptr==0)); + @/do@+{decr(help_ptr);print(help_line[help_ptr]);print_ln(); + }@+ while (!(help_ptr==0)); } help4("Sorry, I already gave what help I could...",@/ "Maybe you should try asking a human?",@/ @@ -2272,7 +2264,7 @@ positions from the right end of a binary computer word. @<Types...@>= typedef int scaled; /*this type is used for scaled integers*/ -typedef uint32_t nonnegative_integer; /*$0\le x<2^{31}$*/ +typedef int32_t nonnegative_integer; /*$0\le x<2^{31}$*/ typedef int8_t small_number; /*this type is self-explanatory*/ @ The following function is used to create a scaled integer from a given decimal @@ -2310,9 +2302,9 @@ if (s < 0) print_int(s/unity); /*print the integer part*/ print_char('.'); s=10*(s%unity)+5;delta=10; -do {if (delta > unity) s=s+0100000-50000; /*round the last digit*/ +@/do@+{if (delta > unity) s=s+0100000-50000; /*round the last digit*/ print_char('0'+(s/unity));s=10*(s%unity);delta=delta*10; -} while (!(s <= delta)); +}@+ while (!(s <= delta)); } @ Physical sizes that a \TeX\ user specifies for portions of documents are @@ -2429,7 +2421,7 @@ computing at most 1095 distinct values, but that is plenty. @d inf_bad 10000 /*infinitely bad value*/ -@p @<Declare \Prote\ arithmetic routines@>@; +@p @<Declare \Prote\ arithmetic routines@>@/ static halfword badness(scaled @!t, scaled @!s) /*compute badness, given |t >= 0|*/ {@+int r; /*approximation to $\alpha t/s$, where $\alpha^3\approx 100\cdot2^{18}$*/ @@ -2464,16 +2456,12 @@ routines cited there must be modified to allow negative glue ratios.) @d unfix(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/ @d fix(A) ((glue_ratio)(A)) /*convert from |double| to type |glue_ratio|*/ @d float_constant(A) ((double)(A)) /*convert |int| constant to |double|*/ -@# -@d perror e@&r@&r@&o@&r /* this is a \.{CWEB} coding trick: */ -@f perror error /* `\&{perror}' will be equivalent to `\&{error}' */ -@f error x /* but `|error|' will not be treated as a reserved word */ @<Types...@>= #if __SIZEOF_FLOAT__==4 typedef float float32_t; #else -#perror @=float type must have size 4@> +#error @=float type must have size 4@> #endif typedef float @!glue_ratio; /*one-word representation of a glue expansion factor*/ @@ -2583,29 +2571,25 @@ typedef uint16_t quarterword; /*1/4 of a word*/ typedef int32_t halfword; /*1/2 of a word*/ typedef int8_t two_choices; /*used when there are two variants in a record*/ typedef int8_t four_choices; /*used when there are four variants in a record*/ -@# -typedef struct { +typedef struct { @;@/ halfword @!rh; union { halfword @!lh; struct { quarterword @!b0;quarterword @!b1;} ; };} two_halves; -@# -typedef struct { +typedef struct { @;@/ quarterword @!b0; quarterword @!b1; quarterword @!b2; quarterword @!b3; } four_quarters; -@# -typedef struct { +typedef struct { @;@/ union { int @!i; glue_ratio @!gr; two_halves @!hh; four_quarters @!qqqq; };} memory_word; -@# typedef struct {@+FILE *f;@+memory_word@,d;@+} word_file; @ When debugging, we may want to print a |memory_word| without knowing @@ -2788,11 +2772,11 @@ one-word nodes that starts at position |p|. {@+pointer @!q, @!r; /*list traversers*/ if (p!=null) {@+r=p; - do {q=r;r=link(r); + @/do@+{q=r;r=link(r); #ifdef @!STAT decr(dyn_used); #endif - } while (!(r==null)); /*now |q| is the last node on the list*/ + }@+ while (!(r==null)); /*now |q| is the last node on the list*/ link(q)=avail;avail=p; } } @@ -2837,11 +2821,11 @@ pointer @!q; /*the node physically after node |p|*/ int @!r; /*the newly allocated node, or a candidate for this honor*/ int @!t; /*temporary register*/ restart: p=rover; /*start at some free node in the ring*/ -do {@<Try to allocate within node |p| and its physical successors, and +@/do@+{@<Try to allocate within node |p| and its physical successors, and |goto found| if allocation was possible@>; @^inner loop@> p=rlink(p); /*move to the next node in the ring*/ -} while (!(p==rover)); /*repeat until the whole list has been traversed*/ +}@+ while (!(p==rover)); /*repeat until the whole list has been traversed*/ if (s==010000000000) {@+return max_halfword; } @@ -3334,7 +3318,7 @@ current \.{\\lineskip}. pointer @!q; /*the glue specification*/ p=get_node(small_node_size);type(p)=glue_node;subtype(p)=n+1; leader_ptr(p)=null;@/ -q=@[@<Current |mem| equivalent of glue parameter number |n|@>@]; +q=@<Current |mem| equivalent of glue parameter number |n|@>@t@>; glue_ptr(p)=q;incr(glue_ref_count(q)); return p; } @@ -3374,8 +3358,6 @@ kerns inserted from font information or math mode calculations) or |explicit| (for kerns inserted from non-math accents) or |mu_glue| (for kerns inserted from \.{\\mkern} specifications in math formulas). -@s explicit true - @d kern_node 11 /*|type| of a kern node*/ @d explicit 1 /*|subtype| of kern nodes from \.{\\kern} and \.{\\/}*/ @d acc_kern 2 /*|subtype| of kern nodes from accents*/ @@ -3591,7 +3573,7 @@ done1: @ @<Check variable-size...@>= p=rover;q=null;clobbered=false; -do {if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true; +@/do@+{if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true; else if ((rlink(p) >= lo_mem_max)||(rlink(p) < mem_min)) clobbered=true; else if (!(is_empty(p))||(node_size(p) < 2)||@| (p+node_size(p) > lo_mem_max)||@|(llink(rlink(p))!=p)) clobbered=true; @@ -3608,7 +3590,7 @@ for (q=p; q<=p+node_size(p)-1; q++) /*mark all locations free*/ is_free[q]=true; } q=p;p=rlink(p); -} while (!(p==rover)); +}@+ while (!(p==rover)); done2: @ @<Check flags...@>= @@ -3703,13 +3685,13 @@ while (p > mem_min) {@+if ((font(p) < font_base)||(font(p) > font_max)) print_char('*'); @.*\relax@> - else@<Print the font identifier for |font(p)|@>@; + else@<Print the font identifier for |font(p)|@>; print_char(' ');font_in_short_display=font(p); } print_ASCII(qo(character(p))); } } - else@<Print a short indication of the contents of node |p|@>@; + else@<Print a short indication of the contents of node |p|@>; p=link(p); } } @@ -3742,7 +3724,7 @@ its reference count, and one to print a rule dimension. {@+if (p > mem_end) print_esc("CLOBBERED."); else{@+if ((font(p) < font_base)||(font(p) > font_max)) print_char('*'); @.*\relax@> - else@<Print the font identifier for |font(p)|@>@; + else@<Print the font identifier for |font(p)|@>; print_char(' ');print_ASCII(qo(character(p))); } } @@ -3882,7 +3864,7 @@ else switch (type(p)) { case disc_node: @<Display discretionary |p|@>@;@+break; case mark_node: @<Display mark |p|@>@;@+break; case adjust_node: @<Display adjustment |p|@>@;@+break; - @/@t\4@>@<Cases of |show_node_list| that arise in mlists only@>@; + @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@; default:print("Unknown node type!"); } @@ -4081,7 +4063,7 @@ specification is being withdrawn. } @p static void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/ -fast_delete_glue_ref(p)@; +fast_delete_glue_ref(p) static void delete_xdimen_ref(pointer @!p) /*|p| points to a xdimen specification*/ {@+if (p==null) return; if (xdimen_ref_count(p)==null) free_node(p, xdimen_node_size); @@ -4122,7 +4104,7 @@ while (p!=null) flush_node_list(post_break(p)); } @+break; case adjust_node: flush_node_list(adjust_ptr(p));@+break; - @/@t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@; + @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@; default:confusion("flushing"); @:this can't happen flushing}{\quad flushing@> } @/ @@ -5022,7 +5004,7 @@ else switch (chr_code) { case every_vbox_loc: print_esc("everyvbox");@+break; case every_job_loc: print_esc("everyjob");@+break; case every_cr_loc: print_esc("everycr");@+break; - @/@t\4@>@<Cases of |assign_toks| for |print_cmd_chr|@>@; + @/@<Cases of |assign_toks| for |print_cmd_chr|@>@/ default:print_esc("errhelp"); } @+break; @@ -5350,7 +5332,7 @@ case right_hyphen_min_code: print_esc("righthyphenmin");@+break; case holding_inserts_code: print_esc("holdinginserts");@+break; case error_context_lines_code: print_esc("errorcontextlines");@+break; case tracing_stack_levels_code: print_esc("tracingstacklevels");@+break; -@/@t\4@>@<Cases for |print_param|@>@; +@/@<Cases for |print_param|@>@/ default:print("[unknown integer parameter!]"); } } @@ -5503,7 +5485,6 @@ defined by |SOURCE_DATE_EPOCH|. @^reference time@> @^system dependencies@> \TeX\ Live calls |tl_now| to obtain the current time as a |tm| structure. -@s tm int @p static void fix_date_and_time(void) {@+ struct tm *t=tl_now(); time=sys_time= t->tm_hour*60+t->tm_min;/*minutes since midnight*/ @@ -5707,9 +5688,8 @@ print_char('=');print_scaled(eqtb[n].sc);print("pt"); @ Here is a procedure that displays the contents of |eqtb[n]| symbolically. -@p @<Declare the procedure called |print_cmd_chr|@>@; +@p @t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/ #ifdef @!STAT -@+@t}\6\4\4{@> static void show_eqtb(pointer @!n) {@+if (n < active_base) print_char('?'); /*this can't happen*/ else if (n < glue_base) @<Show equivalent |n|, in region 1 or 2@>@; @@ -5834,10 +5814,10 @@ found: return p; @ @<Insert a new control...@>= {@+if (text(p) > 0) - {@+do {if (hash_is_full) overflow("hash size", hash_size); + {@+@/do@+{if (hash_is_full) overflow("hash size", hash_size); @:TeX capacity exceeded hash size}{\quad hash size@> decr(hash_used); - } while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/ + }@+ while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/ next(p)=hash_used;p=hash_used; } str_room(l);d=cur_length; @@ -6055,7 +6035,7 @@ case end_group: print_esc("endgroup");@+break; case ex_space: print_esc(" ");@+break; case expand_after: switch (chr_code) { case 0: print_esc("expandafter");@+break; -@/@t\4@>@<Cases of |expandafter| for |print_cmd_chr|@>@; +@/@<Cases of |expandafter| for |print_cmd_chr|@>@/ } @+break; /*there are no other cases*/ case halign: print_esc("halign");@+break; case hrule: print_esc("hrule");@+break; @@ -6075,17 +6055,17 @@ case no_expand: print_esc("noexpand");@+break; case non_script: print_esc("nonscript");@+break; case omit: print_esc("omit");@+break; case radical: print_esc("radical");@+break; -case read_to_cs: if (chr_code==0) print_esc("read"); - else @<Cases of |read| for |print_cmd_chr|@>;@+break; +case read_to_cs: if (chr_code==0) print_esc("read") + @<Cases of |read| for |print_cmd_chr|@>;@+break; case relax: print_esc("relax");@+break; case set_box: print_esc("setbox");@+break; case set_prev_graf: print_esc("prevgraf");@+break; case set_shape: switch (chr_code) { case par_shape_loc: print_esc("parshape");@+break; - @/@t\4@>@<Cases of |set_shape| for |print_cmd_chr|@>@; + @<Cases of |set_shape| for |print_cmd_chr|@>@;@/ } @+break; /*there are no other cases*/ -case the: if (chr_code==0) print_esc("the"); - else @<Cases of |the| for |print_cmd_chr|@>;@+break; +case the: if (chr_code==0) print_esc("the") + @<Cases of |the| for |print_cmd_chr|@>;@+break; case toks_register: @<Cases of |toks_register| for |print_cmd_chr|@>@;@+break; case vadjust: print_esc("vadjust");@+break; case valign: print_esc("valign");@+break; @@ -6106,7 +6086,7 @@ Meanwhile, this is a convenient place to catch up on something we were unable to do before the hash table was defined: @<Print the font identifier for |font(p)|@>= -printn_esc(font_id_text(font(p))); +printn_esc(font_id_text(font(p))) @* Saving and restoring equivalents. The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups @@ -6291,7 +6271,7 @@ case shape_ref: {@+q=equiv_field(w); /*we need to free a \.{\\parshape} block*/ if (q!=null) free_node(q, info(q)+info(q)+1); } @+break; /*such a block is |2 n+1| words long, where |n==info(q)|*/ case box_ref: flush_node_list(equiv_field(w));@+break; -@/@t\4@>@<Cases for |eq_destroy|@>@; +@/@<Cases for |eq_destroy|@>@/ default:do_nothing; } } @@ -6329,7 +6309,7 @@ the call, since |eq_save| makes the necessary test. #ifdef @!STAT #define assign_trace(A, B) if (tracing_assigns > 0) restore_trace(A, B); #else -#define assign_trace(A, B) @[@] +#define assign_trace(A, B) #endif static void eq_define(pointer @!p, quarterword @!t, halfword @!e) @@ -6834,7 +6814,7 @@ case endv: print("end of alignment template");@+break; case spacer: chr_cmd("blank space ")@;@+break; case letter: chr_cmd("the letter ")@;@+break; case other_char: chr_cmd("the character ")@;@+break; -@/@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@; +@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@/ default:print("[unknown command code!]"); } } @@ -7572,7 +7552,7 @@ actions. in_open=0;open_parens=0;max_buf_stack=0; grp_stack[0]=0;if_stack[0]=null; param_ptr=0;max_param_stack=0; -first=buf_size;do {buffer[first]=0;decr(first);} while (!(first==0)); +first=buf_size;@/do@+{buffer[first]=0;decr(first);}@+ while (!(first==0)); scanner_status=normal;warning_index=null;first=1; state=new_line;start=1;index=0;line=0;name=0; force_eof=false; @@ -7783,7 +7763,7 @@ and set |state:=mid_line|@>@;@+break; any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|, otherwise set |state:=mid_line|@>@;@+break; any_state_plus(invalid_char): @<Decry the invalid character and |goto restart|@>@; -@/@t\4@>@<Handle situations involving spaces, braces, changes of state@>@; +@t\4@>@<Handle situations involving spaces, braces, changes of state@>@; default:do_nothing; } @@ -7942,8 +7922,8 @@ the buffer left two or three places. } @ @<Scan ahead in the buffer...@>= -{@+do {cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k); -} while (!((cat!=letter)||(k > limit))); +{@+@/do@+{cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k); +}@+ while (!((cat!=letter)||(k > limit))); @<If an expanded...@>; if (cat!=letter) decr(k); /*now |k| points to first nonletter*/ @@ -8188,7 +8168,7 @@ case top_bot_mark: @<Insert the \(a)appropriate mark text into the scanner@>@;@+ case expand_after: switch (cur_chr) { case 0: @<Expand the token after the next token@>@;@+break; case 1: @<Negate a boolean conditional and |goto reswitch|@>@;@+break; -@/@t\4@>@<Cases for |expandafter|@>@; +@/@<Cases for |expandafter|@>@/ } @+break; /*there are no other cases*/ case no_expand: @<Suppress expansion of the next token@>@;@+break; case cs_name: @<Manufacture a control sequence name@>@;@+break; @@ -8252,9 +8232,9 @@ the variables |p| and |q| are reserved for token-list building. @ @<Manufacture a control...@>= {@+r=get_avail();p=r; /*head of the list of characters*/ incr(incsname_state); -do {get_x_token(); +@/do@+{get_x_token(); if (cur_cs==0) store_new_token(cur_tok); -} while (!(cur_cs!=0)); +}@+ while (!(cur_cs!=0)); if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>; decr(incsname_state); @<Look up the characters of list |r| in the hash table, and set |cur_cs|@>; @@ -8312,15 +8292,15 @@ primitive("endinput", input, 1);@/ @!@:end\_input\_}{\.{\\endinput} primitive@> @ @<Cases of |print_cmd_chr|...@>= -case input: if (chr_code==0) print_esc("input"); - else @<Cases of |input| for |print_cmd_chr|@>@; +case input: if (chr_code==0) print_esc("input") + @/@<Cases of |input| for |print_cmd_chr|@>;@/ else print_esc("endinput");@+break; @ @<Initiate or terminate input...@>= -if (cur_chr==1) force_eof=true; -else @<Cases for |input|@>@; +if (cur_chr==1) force_eof=true +@/@<Cases for |input|@>;@/ else if (name_in_progress) insert_relax(); -else start_input(); +else start_input() @ Sometimes the expansion looks too far ahead, so we want to insert a harmless \.{\\relax} into the user's input. @@ -8530,7 +8510,7 @@ a string that will delimit the next parameter. {@+scanner_status=matching;unbalance=0; long_state=eq_type(cur_cs); if (long_state >= outer_call) long_state=long_state-2; -do {link(temp_head)=null; +@/do@+{link(temp_head)=null; if ((info(r) > match_token+255)||(info(r) < match_token)) s=null; else{@+match_chr=info(r)-match_token;s=link(r);r=s; p=temp_head;m=0; @@ -8538,7 +8518,7 @@ else{@+match_chr=info(r)-match_token;s=link(r);r=s; @<Scan a parameter until its delimiter string has been found; or, if |s=null|, simply scan the delimiter string@>;@/ /*now |info(r)| is a token whose command code is either |match| or |end_match|*/ -} while (!(info(r)==end_match_token)); +}@+ while (!(info(r)==end_match_token)); } @ If |info(r)| is a |match| or |end_match| command, it cannot be equal to @@ -8638,7 +8618,7 @@ the rules. if (s!=r) if (s==null) @<Report an improper use of the macro and abort@>@; else{@+t=s; - do {store_new_token(info(t));incr(m);u=link(t);v=s; + @/do@+{store_new_token(info(t));incr(m);u=link(t);v=s; loop@+{@+if (u==r) if (cur_tok!=info(v)) goto done; else{@+r=link(v);goto resume; @@ -8647,7 +8627,7 @@ if (s!=r) u=link(u);v=link(v); } done: t=link(t); - } while (!(t==r)); + }@+ while (!(t==r)); r=s; /*at this point, no tokens are recently matched*/ } @@ -8739,7 +8719,8 @@ if (cur_cmd!=left_brace) } @ @<Get the next non-blank non-relax non-call token@>= -do get_x_token(); while (!((cur_cmd!=spacer)&&(cur_cmd!=relax))) +@/do@+{get_x_token(); +}@+ while (!((cur_cmd!=spacer)&&(cur_cmd!=relax))) @ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded by optional spaces; `\.{\\relax}' is not ignored here. @@ -8750,7 +8731,8 @@ if (cur_tok!=other_token+'=') back_input(); } @ @<Get the next non-blank non-call token@>= -do get_x_token(); while (!(cur_cmd!=spacer)) +@/do@+{get_x_token(); +}@+ while (!(cur_cmd!=spacer)) @ In case you are getting bored, here is a slightly less trivial routine: Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or @@ -8809,9 +8791,9 @@ have to declare |scan_int| as a |forward| procedure. A few other procedures are also declared at this point. @p static void scan_int(void); /*scans an integer value*/ -@#@<Declare procedures that scan restricted classes of integers@>@; -@<Declare \eTeX\ procedures for scanning@>@; -@<Declare procedures that scan font-related stuff@>@; +@t\4\4@>@<Declare procedures that scan restricted classes of integers@>@; +@t\4\4@>@<Declare \eTeX\ procedures for scanning@>@; +@t\4\4@>@<Declare procedures that scan font-related stuff@>@; @ \TeX\ doesn't know exactly what to expect when |scan_something_internal| begins. For example, an integer or dimension or glue value could occur @@ -9009,9 +8991,9 @@ primitive("badness", last_item, badness_code); @ @<Cases of |print_cmd_chr|...@>= case set_aux: if (chr_code==vmode) print_esc("prevdepth"); @+else print_esc("spacefactor");@+break; -case set_page_int: if (chr_code==0) print_esc("deadcycles"); -else @<Cases of |set_page_int| for |print_cmd_chr|@>@; -else print_esc("insertpenalties");@+break; +case set_page_int: if (chr_code==0) print_esc("deadcycles") +@/@<Cases of |set_page_int| for |print_cmd_chr|@>;@/ +@+else print_esc("insertpenalties");@+break; case set_box_dimen: if (chr_code==width_offset) print_esc("wd"); else if (chr_code==height_offset) print_esc("ht"); else print_esc("dp");@+break; @@ -9020,7 +9002,7 @@ case last_item: switch (chr_code) { case dimen_val: print_esc("lastkern");@+break; case glue_val: print_esc("lastskip");@+break; case input_line_no_code: print_esc("inputlineno");@+break; - @/@t\4@>@<Cases of |last_item| for |print_cmd_chr|@>@; + @/@<Cases of |last_item| for |print_cmd_chr|@>@/ default:print_esc("badness"); } @+break; @@ -9042,8 +9024,8 @@ else if (m==vmode) else scanned_result(space_factor, int_val) @ @<Fetch the |dead_cycles| or the |insert_penalties|@>= -{@+if (m==0) cur_val=dead_cycles; -else @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>@; +{@+if (m==0) cur_val=dead_cycles +@/@<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>;@/ else cur_val=insert_penalties; cur_val_level=int_val; } @@ -9094,14 +9076,14 @@ else if (m >= input_line_no_code) if (m >= eTeX_glue) @<Process an expression and |return|@>@; else if (m >= eTeX_dim) {@+switch (m) { - @/@t\4@>@<Cases for fetching a dimension value@>@; + @/@<Cases for fetching a dimension value@>@/ } /*there are no other cases*/ cur_val_level=dimen_val; } else{@+switch (m) { case input_line_no_code: cur_val=line;@+break; case badness_code: cur_val=last_badness;@+break; - @/@t\4@>@<Cases for fetching an integer value@>@; + @/@<Cases for fetching an integer value@>@/ } /*there are no other cases*/ cur_val_level=int_val; } @@ -9326,11 +9308,11 @@ if (negative) negate(cur_val); @ @<Get the next non-blank non-sign token...@>= negative=false; -do {@<Get the next non-blank non-call token@>; +@/do@+{@<Get the next non-blank non-call token@>; if (cur_tok==other_token+'-') {@+negative=!negative;cur_tok=other_token+'+'; } -} while (!(cur_tok!=other_token+'+')); +}@+ while (!(cur_tok!=other_token+'+')) @ A space is ignored after an alphabetic character constant, so that such constants behave like numeric ones. @@ -9451,7 +9433,7 @@ int @!f; /*numerator of a fraction whose denominator is $2^{16}$*/ f=0;arith_error=false;cur_order=normal;negative=false; cur_hfactor=cur_vfactor=0; if (!shortcut) - {@+@<Get the next non-blank non-sign...@>@; + {@+@<Get the next non-blank non-sign...@>; if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal)) @<Fetch an internal dimension and |goto attach_sign|, or fetch an internal integer@>@; @@ -9690,7 +9672,7 @@ most of the work has already been done. bool negative; /*should the answer be negated?*/ pointer @!q; /*new glue specification*/ bool @!mu; /*does |level==mu_val|?*/ -mu=(level==mu_val);@<Get the next non-blank non-sign...@>@; +mu=(level==mu_val);@<Get the next non-blank non-sign...@>; if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal)) {@+scan_something_internal(level, negative); if (cur_val_level >= glue_val) @@ -9870,7 +9852,7 @@ case convert: switch (chr_code) { case font_name_code: print_esc("fontname");@+break; case job_name_code: print_esc("jobname");@+break; case eTeX_revision_code: print_esc("eTeXrevision");@+break; - @/@t\4@>@<Cases of |convert| for |print_cmd_chr|@>@; + @/@<Cases of |convert| for |print_cmd_chr|@>@/ } @+break; @ The procedure |conv_toks| uses |str_toks| to insert the token list @@ -9901,7 +9883,7 @@ case string_code: case meaning_code: {@+save_scanner_status=scanner_status; case font_name_code: scan_font_ident();@+break; case job_name_code: if (job_name==0) open_log_file();@+break; case eTeX_revision_code: do_nothing;@+break; -@/@t\4@>@<Cases of `Scan the argument for command |c|'@>@; +@/@<Cases of `Scan the argument for command |c|'@>@/ } /*there are no other cases*/ @ @<Print the result of command |c|@>= @@ -9919,7 +9901,7 @@ case font_name_code: {@+printn(font_name[cur_val]); } @+break; case eTeX_revision_code: print(eTeX_revision);@+break; case job_name_code: printn(job_name);@+break; -@/@t\4@>@<Cases of `Print the result of command |c|'@>@; +@/@<Cases of `Print the result of command |c|'@>@/ } /*there are no other cases*/ @ Now we can't postpone the difficulties any longer; we must bravely tackle @@ -10095,13 +10077,13 @@ p=def_ref; /*the reference count*/ store_new_token(end_match_token); if ((n < 0)||(n > 15)) m=16;@+else m=n; s=align_state;align_state=1000000; /*disable tab marks, etc.*/ -do @<Input and store tokens from the next line of the file@>@; -while (!(align_state==1000000)); +@/do@+{@<Input and store tokens from the next line of the file@>; +}@+ while (!(align_state==1000000)); cur_val=def_ref;scanner_status=normal;align_state=s; } @ @<Input and store tokens from the next line of the file@>= -{begin_file_reading();name=m+1; +begin_file_reading();name=m+1; if (read_open[m]==closed) @<Input for \.{\\read} from the terminal@>; else if (read_open[m]==just_open) @<Input the first line of |read_file[m]|@>@; else@<Input the next line of |read_file[m]|@>; @@ -10114,12 +10096,12 @@ loop@+{@+get_token(); if (cur_tok==0) goto done; /*|cur_cmd==cur_chr==0| will occur at the end of the line*/ if (align_state < 1000000) /*unmatched `\.\}' aborts the line*/ - {@+do get_token(); while (!(cur_tok==0)); + {@+@/do@+{get_token();}@+ while (!(cur_tok==0)); align_state=1000000;goto done; } store_new_token(cur_tok); } -done: end_file_reading();} +done: end_file_reading() @ Here we input on-line into the |buffer| array, prompting the user explicitly if |n >= 0|. The value of |n| is set negative so that additional prompts @@ -10237,7 +10219,7 @@ switch (chr_code%unless_code) { case if_true_code: print_esc("iftrue");@+break; case if_false_code: print_esc("iffalse");@+break; case if_case_code: print_esc("ifcase");@+break; - @/@t\4@>@<Cases of |if_test| for |print_cmd_chr|@>@; + @/@<Cases of |if_test| for |print_cmd_chr|@>@/ default:print_esc("if"); } } @+break; @@ -10418,7 +10400,7 @@ case if_eof_code: {@+scan_four_bit_int();b=(read_open[cur_val]==closed); } @+break; case if_true_code: b=true;@+break; case if_false_code: b=false;@+break; -@/@t\4@>@<Cases for |conditional|@>@; +@/@<Cases for |conditional|@>@/ case if_case_code: @<Select the appropriate case and |return| or |goto common_ending|@>; } /*there are no other cases*/ @@ -10795,7 +10777,6 @@ static str_number b_make_name_string(byte_file *f) {@+return make_name_string(); } #ifdef @!INIT -@+@t}\6\4\4{@> static str_number w_make_name_string(word_file *f) {@+return make_name_string(); } @@ -11549,8 +11530,8 @@ that do not do local optimization. @ Font parameters are referred to as |slant(f)|, |space(f)|, etc. -@d param_end(A) @[param_base[A]].sc@] -@d param(A) @[font_info[A+param_end@] +@d param_end(A) param_base[A]].sc +@d param(A) font_info[A+param_end @d slant param(slant_code) /*slant to the right, per unit distance upward*/ @d space param(space_code) /*normal space between words*/ @d space_stretch param(space_stretch_code) /*stretch between words*/ @@ -11954,11 +11935,11 @@ if (cur_val==fmem_ptr) } @ @<Increase the number of parameters...@>= -{@+do {if (fmem_ptr==font_mem_size) +{@+@/do@+{if (fmem_ptr==font_mem_size) overflow("font memory", font_mem_size); @:TeX capacity exceeded font memory}{\quad font memory@> font_info[fmem_ptr].sc=0;incr(fmem_ptr);incr(font_params[f]); -} while (!(n==font_params[f])); +}@+ while (!(n==font_params[f])); cur_val=fmem_ptr-1; /*this equals |param_base[f]+font_params[f]|*/ } @@ -13002,7 +12983,7 @@ entering a new level of recursion. In effect, the values of |save_h| and |save_v| on \TeX's run-time stack correspond to the values of |h| and |v| that a \.{DVI}-reading program will push onto its coordinate stack. -@p @<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@; +@p @t\4@>@<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@/ static void hlist_out(void) /*output an |hlist_node| box*/ {@+ scaled base_line; /*the baseline coordinate for this box*/ @@ -13044,13 +13025,13 @@ reaching a non-|char_node|. The program uses the fact that |set_char_0==0|. @<Output node |p| for |hlist_out|...@>= reswitch: if (is_char_node(p)) {@+synch_h;synch_v; - do {f=font(p);c=character(p); + @/do@+{f=font(p);c=character(p); if (f!=dvi_f) @<Change font |dvi_f| to |f|@>; if (c >= qi(128)) dvi_out(set1); dvi_out(qo(c));@/ cur_h=cur_h+char_width(f, char_info(f, c)); p=link(p); - } while (!(!is_char_node(p))); + }@+ while (!(!is_char_node(p))); dvi_h=cur_h; } else@<Output the non-|char_node| |p| for |hlist_out| and move to the next @@ -13355,7 +13336,7 @@ ready for the |ship_out| routine that gets them started in the first place. @p static void ship_out(pointer @!p) /*output the box |p|*/ {@+ execute_output(p); -@<Flush the box from memory...@>@; +flush_node_list(p); } @ @<Flush the box from memory, showing statistics if requested@>= @@ -13921,20 +13902,21 @@ if (last_badness > vbadness) @ When a box is being appended to the current vertical list, the baselineskip calculation is handled by the |append_to_vlist| routine. -@p static void append_to_vlist(pointer @!b) -{ @+bool height_known; +@p static void append_to_vlist(pointer @!b)@t\2\2@>@/ +{ bool height_known;@t\1@>@/ height_known=(type(b)==hlist_node || type(b)==vlist_node ||@| (type(b)==whatsit_node && subtype(b)==hset_node));@/ if (prev_depth > ignore_depth && height_known)@/ - {@+scaled d; /*deficiency of space between baselines*/ + {@+scaled d;@t\1@> /*deficiency of space between baselines*/ pointer @!p; /*a new glue node*/ - d=width(baseline_skip)-prev_depth-height(b); + {@+d=width(baseline_skip)-prev_depth-height(b); if (d < line_skip_limit) p=new_param_glue(line_skip_code); else{@+p=new_skip_param(baseline_skip_code); width(temp_ptr)=d; /*|temp_ptr==glue_ptr(p)|*/ } link(tail)=p;tail=p; -} @+ else @+if (prev_depth<=unknown_depth || prev_depth>ignore_depth ) @/ + }@+ +} @+ else @+if (prev_depth<=unknown_depth || prev_depth>ignore_depth )@t\2@>@/ {@+pointer p; p=new_baseline_node(baseline_skip, line_skip, line_skip_limit); link(tail)= p;tail= p; @@ -14576,12 +14558,12 @@ be probed in increasing order of height. @<Look at the variants of |(z,x)|; set |f| and |c|...@>= if ((z!=0)||(x!=min_quarterword)) {@+z=z+s+16; - do {z=z-16;g=fam_fnt(z); + @/do@+{z=z-16;g=fam_fnt(z); if (g!=null_font) @<Look at the list of characters starting with |x| in font |g|; set |f| and |c| whenever a better character is found; |goto found| as soon as a large enough variant is encountered@>; - } while (!(z < 16)); + }@+ while (!(z < 16)); } @ @<Look at the list of characters starting with |x|...@>= @@ -14971,8 +14953,8 @@ case rel_noad: case close_noad: case punct_noad: @<Convert \(a)a final |bin_noad| to an |ord_noad|@>; if (type(q)==right_noad) goto done_with_noad; } @+break; -@/@t\4@>@<Cases for noads that can follow a |bin_noad|@>@; -@/@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto +@t\4@>@<Cases for noads that can follow a |bin_noad|@>@; +@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto done_with_node|@>@; default:confusion("mlist1"); @:this can't happen mlist1}{\quad mlist1@> @@ -15732,7 +15714,8 @@ is not a penalty node or a |rel_noad| or absent entirely. @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>= if (new_hlist(q)!=null) {@+link(p)=new_hlist(q); - do p=link(p); while (!(link(p)==null)); + @/do@+{p=link(p); + }@+ while (!(link(p)==null)); } if (penalties) if (link(q)!=null) if (pen < inf_penalty) {@+r_type=type(link(q)); @@ -15957,7 +15940,7 @@ good start. This mostly involves scanning the preamble and putting its information into the preamble list. @^preamble@> -@p @<Declare the procedure called |get_preamble_token|@>@; +@p @t\4@>@<Declare the procedure called |get_preamble_token|@>@t@>@/ static void align_peek(void);@/ static void normal_paragraph(void);@/ static void init_align(void) @@ -16145,7 +16128,8 @@ started, or finishes off the alignment. static void align_peek(void) {@+ restart: align_state=1000000; -do get_x_or_protected(); while (!(cur_cmd!=spacer)); +@/do@+{get_x_or_protected(); +}@+ while (!(cur_cmd!=spacer)); if (cur_cmd==no_align) {@+scan_left_brace();new_save_level(no_align_group); if (mode==-vmode) normal_paragraph(); @@ -16164,7 +16148,7 @@ from internal vertical mode to restricted horizontal mode or vice versa. The |space_factor| and |prev_depth| are not used on this semantic level, but we clear them to zero just to be tidy. -@p @<Declare the procedure called |init_span|@>@; +@p @t\4@>@<Declare the procedure called |init_span|@>@t@>@/ static void init_row(void) {@+push_nest();mode=(-hmode-vmode)-mode; if (mode==-hmode) space_factor=0;@+else prev_depth=0; @@ -16257,7 +16241,8 @@ if (extra_info(cur_align)!=span_code) init_span(p); } align_state=1000000; -do get_x_or_protected(); while (!(cur_cmd!=spacer)); +@/do@+{get_x_or_protected(); +}@+ while (!(cur_cmd!=spacer)); cur_align=p; init_col();return false; } @@ -16351,8 +16336,8 @@ link(end_span)=max_quarterword+1;info(end_span)=null; @ @<Update width entry for spanned columns@>= {@+q=cur_span; -do {incr(n);q=link(link(q)); -} while (!(q==cur_align)); +@/do@+{incr(n);q=link(link(q)); +}@+ while (!(q==cur_align)); if (n > max_quarterword) confusion("256 spans"); /*this can happen, but won't*/ @^system dependencies@> @:this can't happen 256 spans}{\quad 256 spans@> @@ -16409,9 +16394,21 @@ unsave(); /*that |align_group| was for the whole alignment*/ if (nest[nest_ptr-1].mode_field==mmode) o=display_indent; else o=0; @<Go through the preamble list, determining the column widths and changing -the alignrecords to dummy unset boxes@>@; +the alignrecords to dummy unset boxes@>; if (x) -{ /*Handle an alignment that depends on |hsize| or |vsize|*/ +{ @<Handle an alignment that depends on |hsize| or |vsize|@>@; + pop_alignment(); +} +else +{ @<Package the preamble list, to determine the actual tabskip glue amounts, + and let |p| point to this prototype box@>; + @<Set the glue in all the unset boxes of the current list@>; + flush_node_list(p);pop_alignment(); +} +@<Insert the \(c)current list into its environment@>; +} @/ +@t\4@>@<Declare the procedure called |align_peek|@>@; +@ @<Handle an alignment that depends on |hsize| or |vsize|@>= pointer r=get_node(align_node_size); save_ptr=save_ptr-2;pack_begin_line=-mode_line; @@ -16423,18 +16420,6 @@ if (x) align_v(r)= (mode!=-vmode); link(head)=r; tail=r; pack_begin_line=0; - pop_alignment(); -} -else -{ @<Package the preamble list, to determine the actual tabskip glue amounts, - and let |p| point to this prototype box@>; - @<Set the glue in all the unset boxes of the current list@>; - flush_node_list(p);pop_alignment(); -} -@<Insert the \(c)current list into its environment@>; -} -@# -@t\4@>@<Declare the procedure called |align_peek|@>@; @ It's time now to dismantle the preamble list and to compute the column widths. Let $w_{ij}$ be the maximum of the natural widths of all entries @@ -16461,7 +16446,7 @@ value is changed to zero and so is the next tabskip. @<Go through the preamble list,...@>= q=link(preamble); -do {flush_list(u_part(q));flush_list(v_part(q)); +@/do@+{flush_list(u_part(q));flush_list(v_part(q)); p=link(link(q)); if (width(q)==null_flag) @<Nullify |width(q)| and the tabskip glue following this column@>; @@ -16475,7 +16460,7 @@ glue_stretch(q)=0;glue_shrink(q)=0; if (width(q)>max_dimen) x=true; #endif q=p; -} while (!(q==null)); +}@+ while (!(q==null)) @ @<Nullify |width(q)| and the tabskip glue following this column@>= {@+width(q)=0;r=link(q);s=glue_ptr(r); @@ -16487,7 +16472,7 @@ if (s!=zero_glue) @ Merging of two span-node lists is a typical exercise in the manipulation of linearly linked data structures. The essential invariant in the following -|do {| loop is that we want to dispense with node |r|, in |q|'s list, +|@/do@+{| loop is that we want to dispense with node |r|, in |q|'s list, and |u| is its successor; all nodes of |p|'s list up to and including |s| have been processed, and the successor of |s| matches |r| or precedes |r| or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|. @@ -16495,7 +16480,7 @@ or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|. @<Merge the widths...@>= {@+t=width(q)+width(glue_ptr(link(q))); r=info(q);s=end_span;info(s)=p;n=min_quarterword+1; -do {width(r)=width(r)-t;u=info(r); +@/do@+{width(r)=width(r)-t;u=info(r); while (link(r) > n) {@+s=info(s);n=link(info(s))+1; } @@ -16506,7 +16491,7 @@ else{@+if (width(r) > width(info(s))) width(info(s))=width(r); free_node(r, span_node_size); } r=u; -} while (!(r==end_span)); +}@+ while (!(r==end_span)); } @ Now the preamble list has been converted to a list of alternating unset @@ -16523,12 +16508,12 @@ if (mode==-vmode) p=hpack(preamble, saved(1), saved_hfactor(1), saved_vfactor(1), saved(0));overfull_rule=rule_save; } else{@+q=link(preamble); - do {height(q)=width(q);width(q)=0;q=link(link(q)); - } while (!(q==null)); + @/do@+{height(q)=width(q);width(q)=0;q=link(link(q)); + }@+ while (!(q==null)); p=vpack(preamble, saved(1), saved_hfactor(1), saved_vfactor(1), saved(0)); q=link(preamble); - do {width(q)=height(q);height(q)=0;q=link(link(q)); - } while (!(q==null)); + @/do@+{width(q)=height(q);height(q)=0;q=link(link(q)); + }@+ while (!(q==null)); } pack_begin_line=0 @@ -16566,9 +16551,9 @@ else{@+type(q)=vlist_node;height(q)=height(p); glue_order(q)=glue_order(p);glue_sign(q)=glue_sign(p); glue_set(q)=glue_set(p);shift_amount(q)=o; r=link(list_ptr(q));s=link(list_ptr(p)); -do {@<Set the glue in node |r| and change it from an unset node@>; +@/do@+{@<Set the glue in node |r| and change it from an unset node@>; r=link(link(r));s=link(link(s)); -} while (!(r==null)); +}@+ while (!(r==null)); } @ A box made from spanned columns will be followed by tabskip glue nodes and @@ -16900,7 +16885,7 @@ k=1 to 6 do cur_active_width[k]=cur_active_width[k]+mem[q+k].sc|};$$ and we want to do this without the overhead of |for| loops. The |do_all_six| macro makes such six-tuples convenient. -@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6)@; +@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6) @<Glob...@>= static scaled @!active_width0[6], *const @!active_width = @!active_width0-1; @@ -17058,7 +17043,7 @@ loop@+{@+resume: r=link(prev_r); @<If node |r| is of type |delta_node|, update |cur_active_width|, set |prev_r| and |prev_prev_r|, then |goto resume|@>; @<If a line number class has ended, create new active nodes for the best -feasible breaks in that class; then |return| if |r==last_active|, otherwise +feasible breaks in that class; then |return| if |r=last_active|, otherwise compute the new |line_width|@>; @<Consider the demerits for a line from |r| to |cur_p|; deactivate node |r| if it should no longer be active; then |goto resume| if a line from @@ -17811,10 +17796,10 @@ character node. @<Advance \(c)|cur_p| to the node following the present string...@>= {@+prev_p=cur_p; -do {f=font(cur_p); +@/do@+{f=font(cur_p); act_width=act_width+char_width(f, char_info(f, character(cur_p))); cur_p=link(cur_p); -} while (!(!is_char_node(cur_p))); +}@+ while (!(!is_char_node(cur_p))); } @ When node |cur_p| is a glue node, we look at |prev_p| to see whether or not @@ -17839,9 +17824,9 @@ only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes. @<Try to break after a discretionary fragment...@>= {@+s=pre_break(cur_p);disc_width=0; if (s==null) try_break(ex_hyphen_penalty, hyphenated); -else{@+do {@<Add the width of node |s| to |disc_width|@>; +else{@+@/do@+{@<Add the width of node |s| to |disc_width|@>; s=link(s); - } while (!(s==null)); + }@+ while (!(s==null)); act_width=act_width+disc_width; try_break(hyphen_penalty, hyphenated); act_width=act_width-disc_width; @@ -17919,11 +17904,11 @@ if (link(active)!=last_active) @ @<Find an active node...@>= r=link(active);fewest_demerits=awful_bad; -do {if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits) +@/do@+{if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits) {@+fewest_demerits=total_demerits(r);best_bet=r; } r=link(r); -} while (!(r==last_active)); +}@+ while (!(r==last_active)); best_line=line_number(best_bet) @ The adjustment for a desired looseness is a slightly more complicated @@ -17933,7 +17918,7 @@ looseness calculation, independently of the other segments. @<Find the best active node...@>= {@+r=link(active);actual_looseness=0; -do {if (type(r)!=delta_node) +@/do@+{if (type(r)!=delta_node) {@+line_diff=line_number(r)-best_line; if (((line_diff < actual_looseness)&&(looseness <= line_diff))||@| ((line_diff > actual_looseness)&&(looseness >= line_diff))) @@ -17946,7 +17931,7 @@ do {if (type(r)!=delta_node) } } r=link(r); -} while (!(r==last_active)); +}@+ while (!(r==last_active)); best_line=line_number(best_bet); } @@ -17981,14 +17966,14 @@ quarterword @!t; /*used for replacement counts in discretionary nodes*/ int @!pen; /*use when calculating penalties between lines*/ halfword @!cur_line; /*the current line number being justified*/ @<Reverse the links of the relevant passive nodes, setting |cur_p| to the -first breakpoint@>@; +first breakpoint@>; cur_line=prev_graf+1; -do {@<Justify the line ending at breakpoint |cur_p|, and append it to the +@/do@+{@<Justify the line ending at breakpoint |cur_p|, and append it to the current vertical list, together with associated penalties and other insertions@>; incr(cur_line);cur_p=next_break(cur_p); if (cur_p!=null) if (!post_disc_break) @<Prune unwanted nodes at the beginning of the next line@>; -} while (!(cur_p==null)); +}@+ while (!(cur_p==null)); if ((cur_line!=best_line)||(link(temp_head)!=null)) confusion("line breaking"); @:this can't happen line breaking}{\quad line breaking@> @@ -18003,8 +17988,8 @@ Node |r| is the passive node being moved from stack to stack. @<Reverse the links of the relevant passive nodes...@>= q=break_node(best_bet);cur_p=null; -do {r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r; -} while (!(q==null)); +@/do@+{r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r; +}@+ while (!(q==null)) @ Glue and penalty and kern and math nodes are deleted at the beginning of a line, except in the anomalous case that the node to be deleted is actually @@ -18257,8 +18242,8 @@ int @!c; /*character being considered for hyphenation*/ second pass, and |cur_p| points to a glue node. @p -static void hyphenate_word(void) -{ @+pointer @!q, @!s, @!prev_s; /*miscellaneous nodes of temporary interest*/ +static void hyphenate_word(void)@t\2\2@>@/ +{ pointer @!q, @!s, @!prev_s;@t\1@> /*miscellaneous nodes of temporary interest*/ small_number @!j; /*an index into |hc| or |hu|*/ uint8_t @!c; /*character being considered for hyphenation*/ @@ -18692,7 +18677,7 @@ pointer @!hyf_node; /*the hyphen, if it exists*/ @ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero. @<Reconstitute nodes for the hyphenated word...@>= -do {l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1; +@/do@+{l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1; if (hyphen_passed==0) {@+link(s)=link(hold_head); while (link(s) > null) s=link(s); @@ -18702,8 +18687,8 @@ if (hyphen_passed==0) } if (hyphen_passed > 0) @<Create and append a discretionary node as an alternative to the unhyphenated -word, and continue to develop both branches until they become equivalent@>@; -} while (!(j > hn)); +word, and continue to develop both branches until they become equivalent@>; +}@+ while (!(j > hn)); link(s)=q @ In this repeat loop we will insert another discretionary if |hyf[j-1]| is @@ -18717,7 +18702,7 @@ point. (Consider the word `difficult', where the letter `c' is in position |j|.) } @<Create and append a discretionary node as an alternative...@>= -do {r=get_node(small_node_size); +@/do@+{r=get_node(small_node_size); link(r)=link(hold_head);type(r)=disc_node; major_tail=r;r_count=0; while (link(major_tail) > null) advance_major_tail; @@ -18728,7 +18713,7 @@ list and to |major_tail| until synchronization has been achieved@>; @<Move pointer |s| to the end of the current list, and set |replace_count(r)| appropriately@>; hyphen_passed=j-1;link(hold_head)=null; -} while (!(!odd(hyf[j-1]))); +}@+ while (!(!odd(hyf[j-1]))) @ The new hyphen might combine with the previous character via ligature or kern. At this point we have |l-1 <= i < j| and |i < hn|. @@ -18760,7 +18745,7 @@ if (bchar_label[hf]!=non_address) /*put left boundary at beginning of new line* {@+decr(l);c=hu[l];c_loc=l;hu[l]=256; } while (l < j) - {@+do {l=reconstitute(l, hn, bchar, non_char)+1; + {@+@/do@+{l=reconstitute(l, hn, bchar, non_char)+1; if (c_loc > 0) {@+hu[c_loc]=c;c_loc=0; } @@ -18770,7 +18755,7 @@ while (l < j) minor_tail=link(hold_head); while (link(minor_tail) > null) minor_tail=link(minor_tail); } - } while (!(l >= j)); + }@+ while (!(l >= j)); while (l > j) @<Append characters of |hu[j..]| to |major_tail|, advancing~|j|@>; } @@ -18888,10 +18873,10 @@ for (j=0; j<=r_hyf-1; j++) hyf[hn-j]=0 @ @<Store \(m)maximum values in the |hyf| table@>= {@+v=trie_op(z); -do {v=v+op_start[cur_lang];i=l-hyf_distance[v]; +@/do@+{v=v+op_start[cur_lang];i=l-hyf_distance[v]; if (hyf_num[v] > hyf[i]) hyf[i]=hyf_num[v]; v=hyf_next[v]; -} while (!(v==min_quarterword)); +}@+ while (!(v==min_quarterword)); } @ The exception table that is built by \TeX's \.{\\hyphenation} primitive is @@ -18956,10 +18941,10 @@ k=hyph_word[h];if (k==0) goto not_found; if (length(k) < hn) goto not_found; if (length(k)==hn) {@+j=1;u=str_start[k]; - do {if (so(str_pool[u]) < hc[j]) goto not_found; + @/do@+{if (so(str_pool[u]) < hc[j]) goto not_found; if (so(str_pool[u]) > hc[j]) goto done; incr(j);incr(u); - } while (!(j > hn)); + }@+ while (!(j > hn)); @<Insert hyphens as specified in |hyph_list[h]|@>; decr(hn);goto found; } @@ -19085,10 +19070,10 @@ k=hyph_word[h]; if (length(k) < length(s)) goto found; if (length(k) > length(s)) goto not_found; u=str_start[k];v=str_start[s]; -do {if (str_pool[u] < str_pool[v]) goto found; +@/do@+{if (str_pool[u] < str_pool[v]) goto found; if (str_pool[u] > str_pool[v]) goto not_found; incr(u);incr(v); -} while (!(u==str_start[k+1])); +}@+ while (!(u==str_start[k+1])); found: q=hyph_list[h];hyph_list[h]=p;p=q;@/ t=hyph_word[h];hyph_word[h]=s;s=t; not_found: @@ -19376,7 +19361,7 @@ loop@+{@+h=z-c;@/ otherwise |goto not_found|@>; not_found: z=trie_link(z); /*move to the next hole*/ } -found: @<Pack the family into |trie| relative to |h|@>@; +found: @<Pack the family into |trie| relative to |h|@>; } @ By making sure that |trie_max| is at least |h+256|, we can be sure that @@ -19387,9 +19372,9 @@ never be occupied in |trie|, and we will have |trie_max >= trie_link(z)|. if (trie_max < h+256) {@+if (trie_size <= h+256) overflow("pattern memory", trie_size); @:TeX capacity exceeded pattern memory}{\quad pattern memory@> - do {incr(trie_max);trie_taken[trie_max]=false; + @/do@+{incr(trie_max);trie_taken[trie_max]=false; trie_link(trie_max)=trie_max+1;trie_back(trie_max)=trie_max-1; - } while (!(trie_max==h+256)); + }@+ while (!(trie_max==h+256)); } @ @<If all characters of the family fit relative to |h|...@>= @@ -19402,15 +19387,15 @@ goto found @ @<Pack the family into |trie| relative to |h|@>= trie_taken[h]=true;trie_ref[p]=h;q=p; -do {z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z); +@/do@+{z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z); trie_back(r)=l;trie_link(l)=r;trie_link(z)=0; if (l < 256) {@+if (z < 256) ll=z;@+else ll=256; - do {trie_min[l]=r;incr(l); - } while (!(l==ll)); + @/do@+{trie_min[l]=r;incr(l); + }@+ while (!(l==ll)); } q=trie_r[q]; -} while (!(q==0)); +}@+ while (!(q==0)) @ To pack the entire linked trie, we use the following recursive procedure. @^recursion@> @@ -19418,12 +19403,12 @@ q=trie_r[q]; @<Declare procedures for preprocessing hyph...@>= static void trie_pack(trie_pointer @!p) /*pack subtries of a family*/ {@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/ -do {q=trie_l[p]; +@/do@+{q=trie_l[p]; if ((q > 0)&&(trie_ref[q]==0)) {@+first_fit(q);trie_pack(q); } p=trie_r[p]; -} while (!(p==0)); +}@+ while (!(p==0)); } @ When the whole trie has been allocated into the sequential table, we @@ -19441,8 +19426,8 @@ if (trie_max==0) /*no patterns were given*/ else{@+if (hyph_root > 0) trie_fix(hyph_root); if (trie_root > 0) trie_fix(trie_root); /*this fixes the non-holes in |trie|*/ r=0; /*now we will zero out all the holes*/ - do {s=trie_link(r);trie[r]=h;r=s; - } while (!(r > trie_max)); + @/do@+{s=trie_link(r);trie[r]=h;r=s; + }@+ while (!(r > trie_max)); } trie_char(0)=qi('?'); /*make |trie_char(c)!=c| for all |c|*/ @@ -19458,11 +19443,11 @@ static void trie_fix(trie_pointer @!p) /*moves |p| and its siblings into |trie|* ASCII_code @!c; /*another one that need not be saved*/ trie_pointer @!z; /*|trie| reference; this local variable must be saved*/ z=trie_ref[p]; -do {q=trie_l[p];c=so(trie_c[p]); +@/do@+{q=trie_l[p];c=so(trie_c[p]); trie_link(z+c)=trie_ref[q];trie_char(z+c)=qi(c);trie_op(z+c)=trie_o[p]; if (q > 0) trie_fix(q); p=trie_r[p]; -} while (!(p==0)); +}@+ while (!(p==0)); } @ Now let's go back to the easier problem, of building the linked @@ -20061,9 +20046,9 @@ if (page_head!=page_tail) print_scaled(t); if (type(r)==split_up) {@+q=page_head;t=0; - do {q=link(q); + @/do@+{q=link(q); if ((type(q)==ins_node)&&(subtype(q)==subtype(r))) incr(t); - } while (!(q==broken_ins(r))); + }@+ while (!(q==broken_ins(r))); print(", #");print_int(t);print(" might split"); } r=link(r); @@ -20723,7 +20708,8 @@ pop_nest();build_page(); @.Unbalanced output routine@> help2("Your sneaky output routine has problematic {'s and/or }'s.",@/ "I can't handle that very well; good luck.");error(); -do get_token(); while (!(loc==null)); +@/do@+{get_token(); +}@+ while (!(loc==null)); } /*loops forever if reading from a file, since |null==min_halfword <= 0|*/ @ @<Ensure that box 255 is empty after output@>= @@ -20807,7 +20793,7 @@ case hmode+no_boundary: {@+get_x_token(); case hmode+spacer: if (space_factor==1000) goto append_normal_space; else app_space();@+break; case hmode+ex_space: case mmode+ex_space: goto append_normal_space; -@/@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@; +@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@; } /*of the big |case| statement*/ goto big_switch; main_loop: @<Append character |cur_chr| and the following characters (if~any) @@ -21120,7 +21106,7 @@ if ((space_factor >= 2000)&&(xspace_skip!=zero_glue)) else{@+if (space_skip!=zero_glue) main_p=space_skip; else@<Find the glue specification...@>; main_p=new_spec(main_p); - @<Modify the glue specification in |main_p| according to the space factor@>@; + @<Modify the glue specification in |main_p| according to the space factor@>; q=new_glue(main_p);glue_ref_count(main_p)=null; } link(tail)=q;tail=q; @@ -21129,7 +21115,7 @@ link(tail)=q;tail=q; @ @<Modify the glue specification in |main_p| according to the space factor@>= if (space_factor >= 2000) width(main_p)=width(main_p)+extra_space(cur_font); stretch(main_p)=xn_over_d(stretch(main_p), space_factor, 1000); -shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor); +shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor) @ Whew---that covers the main loop. We can now proceed at a leisurely pace through the other combinations of possibilities. @@ -21144,7 +21130,7 @@ any_mode(ignore_spaces): {@+@<Get the next non-blank non-call...@>; goto reswitch; } case vmode+stop: if (its_all_over()) return;@+break; /*this is the only way out*/ -@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param): +@t\4@>@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param): report_illegal_case();@+break; @<Math-only cases in non-math modes, or vice versa@>: insert_dollar_sign();@+break; @t\4@>@<Cases of |main_control| that build boxes and lists@>@; @@ -21497,7 +21483,7 @@ case bottom_level: {@+print_err("Too many }'s"); } @+break; case semi_simple_group: case math_shift_group: case math_left_group: extra_right_brace();@+break; -@/@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed +@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed action@>@; default:confusion("rightbrace"); @:this can't happen rightbrace}{\quad rightbrace@> @@ -21693,7 +21679,7 @@ current vertical list. } else{@+cur_val=box_context-global_box_flag;a=4; } -if (cur_val < 256) @[g_define(box_base+cur_val, box_ref, cur_box)@]; +if (cur_val < 256) define(box_base+cur_val, box_ref, cur_box); else sa_def_box; } @@ -21761,13 +21747,13 @@ else{@+if (!is_char_node(tail)) @ @<Remove the last box...@>= {@+q=head; -do {p=q; +@/do@+{p=q; if (!is_char_node(q)) if (type(q)==disc_node) {@+for (m=1; m<=replace_count(q); m++) p=link(p); if (p==tail) goto done; } q=link(p); -} while (!(q==tail)); +}@+ while (!(q==tail)); cur_box=tail;shift_amount(cur_box)=0; tail=p;link(p)=null; done: ;} @@ -22078,13 +22064,13 @@ if ((mode==vmode)&&(tail==head)) non-glue@>@; else{@+if (!is_char_node(tail)) if (type(tail)==cur_chr) {@+q=head; - do {p=q; + @/do@+{p=q; if (!is_char_node(q)) if (type(q)==disc_node) {@+for (m=1; m<=replace_count(q); m++) p=link(p); if (p==tail) return; } q=link(p); - } while (!(q==tail)); + }@+ while (!(q==tail)); link(p)=null;flush_node_list(tail);tail=p; } } @@ -22125,8 +22111,8 @@ case remove_item: if (chr_code==glue_node) print_esc("unskip"); else print_esc("unpenalty");@+break; case un_hbox: if (chr_code==copy_code) print_esc("unhcopy"); else print_esc("unhbox");@+break; -case un_vbox: if (chr_code==copy_code) print_esc("unvcopy"); - else @<Cases of |un_vbox| for |print_cmd_chr|@>@; +case un_vbox: if (chr_code==copy_code) print_esc("unvcopy") + @<Cases of |un_vbox| for |print_cmd_chr|@>;@/ else print_esc("unvbox");@+break; @ The |un_hbox| and |un_vbox| commands unwrap one of the 256 current boxes. @@ -23167,8 +23153,8 @@ primitive("right", left_right, right_noad); text(frozen_right)=text(cur_val);eqtb[frozen_right]=eqtb[cur_val]; @ @<Cases of |print_cmd_chr|...@>= -case left_right: if (chr_code==left_noad) print_esc("left"); -else @<Cases of |left_right| for |print_cmd_chr|@>@; +case left_right: if (chr_code==left_noad) print_esc("left") +@/@<Cases of |left_right| for |print_cmd_chr|@>;@/ else print_esc("right");@+break; @ @<Cases of |main_control| that build...@>= @@ -23505,8 +23491,8 @@ primitive("xdef", def, 3); @ @<Cases of |print_cmd_chr|...@>= case prefix: if (chr_code==1) print_esc("long"); - else if (chr_code==2) print_esc("outer"); - else @<Cases of |prefix| for |print_cmd_chr|@>@; + else if (chr_code==2) print_esc("outer") + @/@<Cases of |prefix| for |print_cmd_chr|@>;@/ else print_esc("global");@+break; case def: if (chr_code==0) print_esc("def"); else if (chr_code==1) print_esc("gdef"); @@ -23616,7 +23602,7 @@ if ((cur_cmd!=def)&&((a%4!=0)||(j!=0))) since the following routines test for the \.{\\global} prefix as follows. @d global (a >= 4) -@d g_define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C) +@d define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C) @d word_define(A, B) if (global) geq_word_define(A, B);@+else eq_word_define(A, B) @<Adjust \(f)for the setting of \.{\\globaldefs}@>= @@ -23634,7 +23620,8 @@ control sequence for a token that is not redefinable. @<Declare subprocedures for |prefixed_command|@>= static void get_r_token(void) {@+ -restart: do get_token(); while (!(cur_tok!=space_token)); +restart: @/do@+{get_token(); +}@+ while (!(cur_tok!=space_token)); if ((cur_cs==0)||(cur_cs > frozen_control_sequence)) {@+print_err("Missing control sequence inserted"); @.Missing control...@> @@ -23657,7 +23644,7 @@ text(frozen_protection)=s_no("inaccessible"); (Unfortunately, they aren't all as simple as this.) @<Assignments@>= -case set_font: g_define(cur_font_loc, data, cur_chr);@+break; +case set_font: define(cur_font_loc, data, cur_chr);@+break; @ When a |def| command has been scanned, |cur_chr| is odd if the definition is supposed to be global, and @@ -23671,7 +23658,7 @@ case def: {@+if (odd(cur_chr)&&!global&&(global_defs >= 0)) a=a+4; {@+q=get_avail();info(q)=j;link(q)=link(def_ref); link(def_ref)=q; } - g_define(p, call+(a%4), def_ref); + define(p, call+(a%4), def_ref); } @+break; @ Both \.{\\let} and \.{\\futurelet} share the command code |let|. @@ -23689,7 +23676,8 @@ case let: if (chr_code!=normal) print_esc("futurelet");@+else print_esc("let");@ case let: {@+n=cur_chr; get_r_token();p=cur_cs; if (n==normal) - {@+do get_token(); while (!(cur_cmd!=spacer)); + {@+@/do@+{get_token(); + }@+ while (!(cur_cmd!=spacer)); if (cur_tok==other_token+'=') {@+get_token(); if (cur_cmd==spacer) get_token(); @@ -23702,7 +23690,7 @@ case let: {@+n=cur_chr; else if ((cur_cmd==internal_register)||(cur_cmd==toks_register)) if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max)) add_sa_ref(cur_chr); - g_define(p, cur_cmd, cur_chr); + define(p, cur_cmd, cur_chr); } @+break; @ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|; @@ -23757,12 +23745,12 @@ producing an ``undefined control sequence'' error or expanding the previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. @<Assignments@>= -case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;g_define(p, relax, 256); +case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;define(p, relax, 256); scan_optional_equals(); switch (n) { - case char_def_code: {@+scan_char_num();g_define(p, char_given, cur_val); + case char_def_code: {@+scan_char_num();define(p, char_given, cur_val); } @+break; - case math_char_def_code: {@+scan_fifteen_bit_int();g_define(p, math_given, cur_val); + case math_char_def_code: {@+scan_fifteen_bit_int();define(p, math_given, cur_val); } @+break; default:{@+scan_register_num(); if (cur_val > 255) @@ -23770,15 +23758,15 @@ case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;g_define(p, relax, 256); if (j > mu_val) j=tok_val; /*|int_val dotdot mu_val| or |tok_val|*/ find_sa_element(j, cur_val, true);add_sa_ref(cur_ptr); if (j==tok_val) j=toks_register;@+else j=internal_register; - g_define(p, j, cur_ptr); + define(p, j, cur_ptr); } else switch (n) { - case count_def_code: g_define(p, assign_int, count_base+cur_val);@+break; - case dimen_def_code: g_define(p, assign_dimen, scaled_base+cur_val);@+break; - case skip_def_code: g_define(p, assign_glue, skip_base+cur_val);@+break; - case mu_skip_def_code: g_define(p, assign_mu_glue, mu_skip_base+cur_val);@+break; - case toks_def_code: g_define(p, assign_toks, toks_base+cur_val); + case count_def_code: define(p, assign_int, count_base+cur_val);@+break; + case dimen_def_code: define(p, assign_dimen, scaled_base+cur_val);@+break; + case skip_def_code: define(p, assign_glue, skip_base+cur_val);@+break; + case mu_skip_def_code: define(p, assign_mu_glue, mu_skip_base+cur_val);@+break; + case toks_def_code: define(p, assign_toks, toks_base+cur_val); } /*there are no other cases*/ } } @@ -23794,7 +23782,7 @@ case read_to_cs: {@+j=cur_chr;scan_int();n=cur_val; "I'm going to look for the \\cs now.");error(); } get_r_token(); - p=cur_cs;read_toks(n, p, j);g_define(p, call, cur_val); + p=cur_cs;read_toks(n, p, j);define(p, call, cur_val); } @+break; @ The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive @@ -23864,7 +23852,7 @@ case assign_dimen: {@+p=cur_chr;scan_optional_equals(); case assign_glue: case assign_mu_glue: {@+p=cur_chr;n=cur_cmd;scan_optional_equals(); if (n==assign_mu_glue) scan_glue(mu_val);@+else scan_glue(glue_val); trap_zero_glue(); - g_define(p, glue_ref, cur_val); + define(p, glue_ref, cur_val); } @+break; @ When a glue register or parameter becomes zero, it will always point to @@ -23927,8 +23915,8 @@ case def_code: {@+@<Let |n| be the largest legal code value, based on |cur_chr|@ help1("I'm going to use 0 instead of that illegal code value.");@/ error();cur_val=0; } - if (p < math_code_base) g_define(p, data, cur_val); - else if (p < del_code_base) g_define(p, data, hi(cur_val)); + if (p < math_code_base) define(p, data, cur_val); + else if (p < del_code_base) define(p, data, hi(cur_val)); else word_define(p, cur_val); } @+break; @@ -23941,7 +23929,7 @@ else n=255 @ @<Assignments@>= case def_family: {@+p=cur_chr;scan_four_bit_int();p=p+cur_val; - scan_optional_equals();scan_font_ident();g_define(p, data, cur_val); + scan_optional_equals();scan_font_ident();define(p, data, cur_val); } @+break; @ Next we consider changes to \TeX's numeric registers. @@ -24149,8 +24137,8 @@ static void alter_integer(void) {@+small_number c; /*0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.*/ c=cur_chr;scan_optional_equals();scan_int(); -if (c==0) dead_cycles=cur_val; -else @<Cases for |alter_integer|@>@; +if (c==0) dead_cycles=cur_val +@/@<Cases for |alter_integer|@>@;@/ else insert_penalties=cur_val; } @@ -24188,7 +24176,7 @@ case set_shape: {@+q=cur_chr;scan_optional_equals();scan_int();n=cur_val; } cur_hfactor=fh; cur_vfactor=fv; } - g_define(q, shape_ref, p); + define(q, shape_ref, p); } @+break; @ Here's something that isn't quite so obvious. It guarantees that @@ -24219,7 +24207,7 @@ new_patterns();goto done;@; print_err("Patterns can be loaded only by INITEX"); @.Patterns can be...@> help0;error(); - do get_token(); while (!(cur_cmd==right_brace)); /*flush the patterns*/ + @/do@+{get_token();}@+ while (!(cur_cmd==right_brace)); /*flush the patterns*/ return; } else{@+new_hyph_exceptions();goto done; @@ -24273,12 +24261,12 @@ else{@+old_setting=selector;selector=new_string; @.FONTx@> str_room(1);t=make_string(); } -g_define(u, set_font, null_font);scan_optional_equals();scan_file_name(); +define(u, set_font, null_font);scan_optional_equals();scan_file_name(); @<Scan the font size specification@>; @<If this font has already been loaded, set |f| to the internal font number and |goto common_ending|@>; f=read_font_info(u, cur_name, cur_area, s); -common_ending: g_define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t; +common_ending: define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t; } @ @<Scan the font size specification@>= @@ -24663,7 +24651,7 @@ so that the inverse relation between them is clear. The global variable |format_ident| is a string that is printed right after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this string says simply `\.{ (INITEX)}'; for other versions of \TeX\ it says, -for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year, +for example, `\.{ (preloaded format=plain 1982.11.19)}', showing the year, month, and day that the format file was created. We have |format_ident==0| before \TeX's tables are loaded. @@ -24853,10 +24841,10 @@ sort_avail();var_used=0; dump_int(lo_mem_max);dump_int(rover); if (eTeX_ex) for (k=int_val; k<=tok_val; k++) dump_int(sa_root[k]); p=mem_bot;q=rover;x=0; -do {for (k=p; k<=q+1; k++) dump_wd(mem[k]); +@/do@+{for (k=p; k<=q+1; k++) dump_wd(mem[k]); x=x+q+2-p;var_used=var_used+q-p; p=q+node_size(q);q=rlink(q); -} while (!(q==rover)); +}@+ while (!(q==rover)); var_used=var_used+lo_mem_max-p;dyn_used=mem_end+1-hi_mem_min;@/ for (k=p; k<=lo_mem_max; k++) dump_wd(mem[k]); x=x+lo_mem_max+1-p; @@ -24878,11 +24866,11 @@ undump(lo_mem_stat_max+1, lo_mem_max, rover); if (eTeX_ex) for (k=int_val; k<=tok_val; k++) undump(null, lo_mem_max, sa_root[k]); p=mem_bot;q=rover; -do {for (k=p; k<=q+1; k++) undump_wd(mem[k]); +@/do@+{for (k=p; k<=q+1; k++) undump_wd(mem[k]); p=q+node_size(q); if ((p > lo_mem_max)||((q >= rlink(q))&&(rlink(q)!=rover))) goto bad_fmt; q=rlink(q); -} while (!(q==rover)); +}@+ while (!(q==rover)); for (k=p; k<=lo_mem_max; k++) undump_wd(mem[k]); if (mem_min < mem_bot-2) /*make more low memory available*/ {@+p=llink(rover);q=mem_min+1; @@ -24897,14 +24885,14 @@ for (k=hi_mem_min; k<=mem_end; k++) undump_wd(mem[k]); undump_int(var_used);undump_int(dyn_used) @ @<Dump the table of equivalents@>= -@<Dump regions 1 to 4 of |eqtb|@>@; -@<Dump regions 5 and 6 of |eqtb|@>@; +@<Dump regions 1 to 4 of |eqtb|@>; +@<Dump regions 5 and 6 of |eqtb|@>; dump_int(par_loc);dump_int(write_loc);@/ dump_int(input_loc);@/ @<Dump the hash table@>@; @ @<Undump the table of equivalents@>= -@<Undump regions 1 to 6 of |eqtb|@>@; +@<Undump regions 1 to 6 of |eqtb|@>; undump(hash_base, frozen_control_sequence, par_loc); par_token=cs_token_flag+par_loc;@/ undump(hash_base, frozen_control_sequence, write_loc);@/ @@ -24919,7 +24907,7 @@ copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. @<Dump regions 1 to 4 of |eqtb|@>= k=active_base; -do {j=k; +@/do@+{j=k; while (j < int_base-1) {@+if ((equiv(j)==equiv(j+1))&&(eq_type(j)==eq_type(j+1))&&@| (eq_level(j)==eq_level(j+1))) goto found1; @@ -24937,10 +24925,10 @@ while (k < l) {@+dump_wd(eqtb[k]);incr(k); } k=j+1;dump_int(k-l); -} while (!(k==int_base)); +}@+ while (!(k==int_base)) @ @<Dump regions 5 and 6 of |eqtb|@>= -do {j=k; +@/do@+{j=k; while (j < eqtb_size) {@+if (eqtb[j].i==eqtb[j+1].i) goto found2; incr(j); @@ -24956,11 +24944,11 @@ while (k < l) {@+dump_wd(eqtb[k]);incr(k); } k=j+1;dump_int(k-l); -} while (!(k > eqtb_size)); +}@+ while (!(k > eqtb_size)) @ @<Undump regions 1 to 6 of |eqtb|@>= k=active_base; -do {undump_int(x); +@/do@+{undump_int(x); if ((x < 1)||(k+x > eqtb_size+1)) goto bad_fmt; for (j=k; j<=k+x-1; j++) undump_wd(eqtb[j]); k=k+x; @@ -24968,7 +24956,7 @@ undump_int(x); if ((x < 0)||(k+x > eqtb_size+1)) goto bad_fmt; for (j=k; j<=k+x-1; j++) eqtb[j]=eqtb[k-1]; k=k+x; -} while (!(k > eqtb_size)); +}@+ while (!(k > eqtb_size)) @ A different scheme is used to compress the hash table, since its lower region is usually sparse. When |text(p)!=0| for |p <= hash_used|, we output @@ -24986,8 +24974,8 @@ print_ln();print_int(cs_count);print(" multiletter control sequences") @ @<Undump the hash table@>= undump(hash_base, frozen_control_sequence, hash_used);p=hash_base-1; -do {undump(p+1, hash_used, p);undump_hh(hash[p]); -} while (!(p==hash_used)); +@/do@+{undump(p+1, hash_used, p);undump_hh(hash[p]); +}@+ while (!(p==hash_used)); for (p=hash_used+1; p<=undefined_control_sequence-1; p++) undump_hh(hash[p]); undump_int(cs_count) @@ -25800,7 +25788,7 @@ case extension: switch (chr_code) { case ignore_node: print("[HINT internal: ignore]");@+break; case immediate_code: print_esc("immediate");@+break; case set_language_code: print_esc("setlanguage");@+break; - @/@t\4@>@<Cases of |extension| for |print_cmd_chr|@>@; + @/@<Cases of |extension| for |print_cmd_chr|@>@/ default:print("[unknown extension!]"); } @+break; @@ -25991,7 +25979,7 @@ case xdimen_node: case ignore_node: @+break; case immediate_code: @<Implement \.{\\immediate}@>@;@+break; case set_language_code: @<Implement \.{\\setlanguage}@>@;@+break; -@/@t\4@>@<Cases for |do_extension|@>@; +@/@<Cases for |do_extension|@>@/ default:confusion("ext1"); @:this can't happen ext1}{\quad ext1@> } @@ -26122,7 +26110,7 @@ case language_node: {@+print_esc("setlanguage"); print_int(what_lhm(p));print_char(','); print_int(what_rhm(p));print_char(')'); } @+break; -@/@t\4@>@<Cases for displaying the |whatsit| node@>@; +@/@<Cases for displaying the |whatsit| node@>@/ case param_node: print_esc("parameter "); print_int(param_type(p));print_char(',');print_int(param_no(p)); print_char(':');print_int(param_value(p).i); @@ -26263,7 +26251,7 @@ case write_node: case special_node: case latespecial_node: {@+r=get_node(write_n case close_node: case language_node: {@+r=get_node(small_node_size); words=small_node_size; } @+break; -@/@t\4@>@<Cases for making a partial copy of the whatsit node@>@; +@/@<Cases for making a partial copy of the whatsit node@>@/ case param_node: {@+r=get_node(param_node_size); if (param_type(p)==glue_type) add_glue_ref(param_value(p).i); @@ -26446,7 +26434,7 @@ case stream_node: free_node(p,stream_node_size); @+break; case xdimen_node: free_node(p,xdimen_node_size); -@/@t\4@>@<Cases for wiping out the whatsit node@>@; +@/@<Cases for wiping out the whatsit node@>@/ default:confusion("ext3"); @:this can't happen ext3}{\quad ext3@> } @/ @@ -26459,7 +26447,7 @@ goto done; @ @<Let |d| be the width of the whatsit |p|@>=d=0 -@ @d adv_past(A) if (subtype(A)==language_node) +@ @d adv_past(A) @+if (subtype(A)==language_node) {@+cur_lang=what_lang(A);l_hyf=what_lhm(A);r_hyf=what_rhm(A); set_hyph_index; } @@ -26558,7 +26546,8 @@ end_token_list() /*conserve stack space*/ @.Unbalanced write...@> help2("On this page there's a \\write with fewer real {'s than }'s.",@/ "I can't handle that very well; good luck.");error(); -do get_token(); while (!(cur_tok==end_write_token)); +@/do@+{get_token(); +}@+ while (!(cur_tok==end_write_token)); } @ The |out_what| procedure takes care of outputting whatsit nodes for @@ -27029,9 +27018,9 @@ a=1; print_nl("");print_ln(); loop@+{@+print_nl("### ");print_group(true); if (cur_group==bottom_level) goto done; - do {m=nest[p].mode_field; + @/do@+{m=nest[p].mode_field; if (p > 0) decr(p);else m=vmode; - } while (!(m!=hmode)); + }@+ while (!(m!=hmode)); print(" ("); switch (cur_group) { case simple_group: {@+incr(p);goto found2; @@ -27156,7 +27145,7 @@ scanner_status=s;warning_index=w;def_ref=d; @ The \.{\\showtokens} command displays a token list. -@d show_tokens 5 /* \.{\\showtokens}, must be odd! */ +@d show_tokens 5 /* \.{\\showtokens} , must be odd! */ @<Generate all \eTeX...@>= primitive("showtokens", xray, show_tokens); @@ -27179,9 +27168,9 @@ primitive("unexpanded", the, 1);@/ primitive("detokenize", the, show_tokens);@/ @!@:detokenize\_}{\.{\\detokenize} primitive@> -@ @<Cases of |the| for |print_cmd_chr|@>= -if (chr_code==1) print_esc("unexpanded"); -else print_esc("detokenize"); +@ @<Cases of |the| for |print_cmd_chr|@>=; +else if (chr_code==1) print_esc("unexpanded"); +else print_esc("detokenize") @ @<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>= if (odd(cur_chr)) @@ -27216,14 +27205,14 @@ case show_ifs: {@+begin_diagnostic();print_nl("");print_ln(); {@+print_nl("### ");print("no active conditionals"); } else{@+p=cond_ptr;n=0; - do {incr(n);p=link(p);@+} while (!(p==null)); + @/do@+{incr(n);p=link(p);@+}@+ while (!(p==null)); p=cond_ptr;t=cur_if;l=if_line;m=if_limit; - do {print_nl("### level ");print_int(n);print(": "); + @/do@+{print_nl("### level ");print_int(n);print(": "); print_cmd_chr(if_test, t); if (m==fi_code) print_esc("else"); print_if_line(l); decr(n);t=subtype(p);l=if_line_field(p);m=type(p);p=link(p); - } while (!(p==null)); + }@+ while (!(p==null)); } } @+break; @@ -27234,17 +27223,17 @@ interaction mode. primitive("interactionmode", set_page_int, 2); @!@:interaction\_mode\_}{\.{\\interactionmode} primitive@> -@ @<Cases of |set_page_int| for |print_cmd_chr|@>= -if (chr_code==2) print_esc("interactionmode"); +@ @<Cases of |set_page_int| for |print_cmd_chr|@>=; +else if (chr_code==2) print_esc("interactionmode") -@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>= -if (m==2) cur_val=interaction; +@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>=; +else if (m==2) cur_val=interaction @ @<Declare \eTeX\ procedures for use...@>= static void new_interaction(void); -@ @<Cases for |alter_integer|@>= -if (c==2) +@ @<Cases for |alter_integer|@>=; +else if (c==2) {@+if ((cur_val < batch_mode)||(cur_val > error_stop_mode)) {@+print_err("Bad interaction mode"); @.Bad interaction mode@> @@ -27263,8 +27252,8 @@ delimiters to appear between \.{\\left} and \.{\\right}. primitive("middle", left_right, middle_noad); @!@:middle\_}{\.{\\middle} primitive@> -@ @<Cases of |left_right| for |print_cmd_chr|@>= -if (chr_code==middle_noad) print_esc("middle"); +@ @<Cases of |left_right| for |print_cmd_chr|@>=; +else if (chr_code==middle_noad) print_esc("middle") @ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. @@ -27273,11 +27262,11 @@ primitive. primitive("scantokens", input, 2); @!@:scan\_tokens\_}{\.{\\scantokens} primitive@> -@ @<Cases of |input| for |print_cmd_chr|@>= -if (chr_code==2) print_esc("scantokens"); +@ @<Cases of |input| for |print_cmd_chr|@>=; +else if (chr_code==2) print_esc("scantokens") -@ @<Cases for |input|@>= -if (cur_chr==2) pseudo_start(); +@ @<Cases for |input|@>=; +else if (cur_chr==2) pseudo_start() @ The global variable |pseudo_files| is used to maintain a stack of pseudo files. The |info| field of each pseudo file points to a linked @@ -27403,8 +27392,8 @@ while (pseudo_files!=null) pseudo_close(); /*flush pseudo files*/ primitive("readline", read_to_cs, 1);@/ @!@:read\_line\_}{\.{\\readline} primitive@> -@ @<Cases of |read| for |print_cmd_chr|@>= -print_esc("readline"); +@ @<Cases of |read| for |print_cmd_chr|@>=; +else print_esc("readline") @ @<Handle \.{\\readline} and |goto done|@>= if (j==1) @@ -27479,9 +27468,9 @@ preceding the mandatory \.{\\endcsname} have been expanded). @<Cases for |conditional|@>= case if_cs_code: {@+n=get_avail();p=n; /*head of the list of characters*/ - do {get_x_token(); + @/do@+{get_x_token(); if (cur_cs==0) store_new_token(cur_tok); - } while (!(cur_cs!=0)); + }@+ while (!(cur_cs!=0)); if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>; @<Look up the characters of list |n| in the hash table, and set |cur_cs|@>; flush_list(n); @@ -27523,8 +27512,8 @@ or during \.{\\write}. primitive("protected", prefix, 8); @!@:protected\_}{\.{\\protected} primitive@> -@ @<Cases of |prefix| for |print_cmd_chr|@>= -if (chr_code==8) print_esc("protected"); +@ @<Cases of |prefix| for |print_cmd_chr|@>=; +else if (chr_code==8) print_esc("protected") @ The |get_x_or_protected| procedure is like |get_x_token| except that protected macros are not expanded. @@ -27762,7 +27751,7 @@ resume: if (s==expr_none) o=l;@+else o=int_val; found: @<Scan the next operator and set |o|@>; arith_error=b; @<Make sure that |f| is in the proper range@>; -switch (s) {@t\4@>@<Cases for evaluation of the current term@>@; +switch (s) {@<Cases for evaluation of the current term@>@; } /*there are no other cases*/ if (o > expr_sub) s=o;@+else@<Evaluate the current expression@>; b=arith_error; @@ -28423,12 +28412,12 @@ else{@+if (sa_index(q) < mu_val_limit) else if (sa_ptr(q)!=null) return; s=pointer_node_size; } -do {i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s); +@/do@+{i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s); if (q==null) /*the whole tree has been freed*/ {@+sa_root[i]=null;return; } delete_sa_ptr;s=index_node_size; /*node |q| is an index node*/ -} while (!(sa_used(q) > 0)); +}@+ while (!(sa_used(q) > 0)); } @ The |print_sa_num| procedure prints the register number corresponding @@ -28531,7 +28520,7 @@ if (l < 4) /*|q| is an index node*/ } else /*|q| is the node for a mark class*/ {@+switch (a) { - @t\4@>@<Cases for |do_marks|@>@; + @<Cases for |do_marks|@>@; } /*there are no other cases*/ if (sa_bot_mark(q)==null) if (sa_split_bot_mark(q)==null) {@+free_node(q, mark_class_node_size);q=null; @@ -28808,7 +28797,7 @@ at by |sa_chain| @<Declare \eTeX\ procedures for tr...@>= static void sa_restore(void) {@+pointer p; /*sparse array element*/ -do {p=sa_loc(sa_chain); +@/do@+{p=sa_loc(sa_chain); if (sa_lev(p)==level_one) {@+if (sa_index(p) >= dimen_val_limit) sa_destroy(sa_chain); #ifdef @!STAT @@ -28831,7 +28820,7 @@ delete_sa_ref(p); p=sa_chain;sa_chain=link(p); if (sa_index(p) < dimen_val_limit) free_node(p, word_node_size); else free_node(p, pointer_node_size); -} while (!(sa_chain==null)); +}@+ while (!(sa_chain==null)); } @ When reading \.{\\patterns} while \.{\\savinghyphcodes} is positive @@ -28852,8 +28841,8 @@ hyph_root=0;hyph_start=0; @ @<Store hyphenation codes for current language@>= {@+c=cur_lang;first_child=false;p=0; -do {q=p;p=trie_r[q]; -} while (!((p==0)||(c <= so(trie_c[p])))); +@/do@+{q=p;p=trie_r[q]; +}@+ while (!((p==0)||(c <= so(trie_c[p])))); if ((p==0)||(c < so(trie_c[p]))) @<Insert a new trie node between |q| and |p|, and make |p| point to it@>; q=p; /*now node |q| represents |cur_lang|*/ @@ -28931,9 +28920,9 @@ primitive("pagediscards", un_vbox, last_box_code);@/ primitive("splitdiscards", un_vbox, vsplit_code);@/ @!@:split\_discards\_}{\.{\\splitdiscards} primitive@> -@ @<Cases of |un_vbox| for |print_cmd_chr|@>= -if (chr_code==last_box_code) print_esc("pagediscards"); -else if (chr_code==vsplit_code) print_esc("splitdiscards"); +@ @<Cases of |un_vbox| for |print_cmd_chr|@>=; +else if (chr_code==last_box_code) print_esc("pagediscards"); +else if (chr_code==vsplit_code) print_esc("splitdiscards") @ @<Handle saved items and |goto done|@>= {@+link(tail)=disc_ptr[cur_chr];disc_ptr[cur_chr]=null; @@ -28993,7 +28982,7 @@ expand_depth_count=0; @* The extended features of \Prote. \Prote\ extends furthermore \eTeX\ i.e. \eTeX\ is thus required before adding \Prote\ own extensions. But if \eTeX\ mode has not -be enabled, the engine is still compatible with \TeX\ with no added +be enabled, the engine is still compatible with \TeX with no added primitive commands and with a modification of code---from \eTeX\ exclusively for now---that is sufficiently minor so that the engine still deserves the name \TeX. @@ -29288,7 +29277,7 @@ hash since |no_new_control_sequence| is |true|), but since it has the for a control sequence entered but never defined. @ @<Cases for |conditional|@>= -case if_primitive_code: {@+do get_token(); while (!(cur_tok!=space_token)); +case if_primitive_code: {@+@/do@+{get_token();}@+ while (!(cur_tok!=space_token)); if ((cur_cs!=0)&&(cur_cmd!=undefined_cs)&&(cur_cmd < call)) b=true;else b=false; } @+break; @@ -29650,7 +29639,7 @@ dependent. The information shall be set in |xchg_buffer|. @^system dependencies@> In this basic implementation, we set the string to the empty one by -simply setting |xchg_buffer_length| to~$0$. +simply setting |xchg_buffer_length| to $0$. @d get_file_mtime xchg_buffer_length=0 @@ -29727,10 +29716,10 @@ case file_dump_code: { FILE *f=fopen((char*)name_of_file0,"rb"); if (f!=NULL) {@+ fseek(f,k,SEEK_SET); - do {i=fgetc(f); if (i==EOF) break; + do@+{i=fgetc(f); if (i==EOF) break; dig[0]=i%16;dig[1]=i/16; print_the_digs(2);decr(l); - } while (!(feof(f)||(l==0))); + }@+ while (!(feof(f)||(l==0))); fclose(f); } } @+break; @@ -29830,7 +29819,7 @@ communicate with the routines. @ To obtain the MD5 hash signature of a file will need an external implementation, since the algorithm requires bitwise operation that -standard \PASCAL\ does not provide. So we do not bother to try. The +standard \PASCAL does not provide. So we do not bother to try. The present implementation returns nothing. @^system dependencies@> @@ -30015,7 +30004,7 @@ else{@+n=(n-1)*mpfract_one; } } -@ The |do {| loop here preserves the following invariant relations +@ The |@/do@+{| 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$. @@ -30030,11 +30019,11 @@ in a register, not store it in memory. @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>= f=1; -do {be_careful=p-q;p=be_careful+p; +@/do@+{be_careful=p-q;p=be_careful+p; if (p >= 0) f=f+f+1; else{@+double(f);p=p+q; } -} while (!(f >= mpfract_one)); +}@+ while (!(f >= mpfract_one)); be_careful=p-q; if (be_careful+p >= 0) incr(f) @@ -30057,7 +30046,7 @@ else{@+n=f/mpfract_one;f=f%mpfract_one; } } f=f+mpfract_one; -@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>@; +@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>; be_careful=n-el_gordo; if (be_careful+p > 0) {@+arith_error=true;n=el_gordo-p; @@ -30082,12 +30071,12 @@ $f_0$ is the original value of~$f$; (ii)~$2^k\L f<2^{k+1}$. @<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>= p=mpfract_half; /*that's $2^{27}$; the invariants hold now with $k=28$*/ if (q < mpfract_four) - do {if (odd(f)) p=halfp(p+q);@+else p=halfp(p); + @/do@+{if (odd(f)) p=halfp(p+q);@+else p=halfp(p); f=halfp(f); - } while (!(f==1)); -else do {if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p); + }@+ while (!(f==1)); +else@/do@+{if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p); f=halfp(f); - } while (!(f==1)); + }@+ while (!(f==1)) @ 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}$, @@ -30275,15 +30264,15 @@ can readily be obtained with the ratio method (Algorithm 3.4.1R in static scaled norm_rand(void) {@+int @!x, @!u, @!l; /*what the book would call $2^{16}X$, $2^{28}U$, and $-2^{24}\ln U$*/ -do { - do {next_random; +@/do@+{ + @/do@+{next_random; x=take_mpfract(112429, randoms[j_random]-mpfract_half); /*$2^{16}\sqrt{8/e}\approx 112428.82793$*/ next_random;u=randoms[j_random]; - } while (!(abs(x) < u)); + }@+ while (!(abs(x) < u)); x=make_mpfract(x, u); l=139548960-m_log(u); /*$2^{24}\cdot12\ln2\approx139548959.6165$*/ -} while (!(ab_vs_cd(1024, l, x, x) >= 0)); +}@+ while (!(ab_vs_cd(1024, l, x, x) >= 0)); return x; } @@ -31171,8 +31160,8 @@ if (option_no_empty_page && ((eject && penalty(p)>2*(eject_penalty)) || (page_contents==empty && !is_visible(p)))) { pointer r, prev_r = p; - loop@+{ - r =link(prev_r); + while (true) + { r =link(prev_r); if (r==null) return; else if (is_visible(r)) break; else if (type(r)==penalty_node && penalty(r)<=eject_penalty) @@ -32017,14 +32006,15 @@ static pointer new_setpage_node(uint8_t i, str_number n) for (p=link(prev_p); p!=null; prev_p=p,p=link(p)) if (setpage_id(p)==i) break; if (p==null) - @<Allocate a new |setpage_node| |p|@>@; + @<allocate a new |setpage_node| |p|@>@; else link(prev_p)=link(p); link(p)=link(setpage_head); link(setpage_head)=p; return p; } -@ @<Allocate a new |setpage_node| |p|@>= +@ +@<allocate a new |setpage_node| |p|@>= { p=get_node(setpage_node_size);type(p)=whatsit_node;subtype(p)=setpage_node; setpage_number(p)=max_ref[page_kind]=++max_page; setpage_id(p)=i; @@ -32111,7 +32101,7 @@ static void hint_open(void) output_file_name=make_name_string(); DBG(DBGBASIC,"Output file %s opened\n",(char *)name_of_file+1); } -@# + #define HITEX_VERSION "1.1" static void hput_definitions(); extern int option_global; @@ -32326,7 +32316,7 @@ but we will need it repeatedly in the function |hdef_param_node| and the overhea not warrant having the mapping in both directions. @<Hi\TeX\ variables@>= -static const int hmap_int[] ={@t\1\1@>@/ +static const int hmap_int[] ={@/ pretolerance_no, /* |pretolerance_code| 0 */ tolerance_no, /* |tolerance_code| 1 */ line_penalty_no, /* |line_penalty_code| 2 */ @@ -32369,7 +32359,7 @@ year_no, /* |year_code| 23 */ -1, /* |output_penalty_code| 39 */ -1, /* |max_dead_cycles_code| 40 */ hang_after_no, /* |hang_after_code| 41*/ -@t\2\2@> floating_penalty_no /* |floating_penalty_code| 42*/ +floating_penalty_no /* |floating_penalty_code| 42*/ }; @@ -32381,7 +32371,7 @@ hang_after_no, /* |hang_after_code| 41*/ if ( hmap_int[i]>=0) int_defined[hmap_int[i]]=int_par(i); max_ref[int_kind]=MAX_INT_DEFAULT; @ The function |hget_int_no| tries to allocate a predefined integer number; -if not successful, it returns~$-1$. +if not successful, it returns $-1$. @<Hi\TeX\ auxiliary routines@>= static int hget_int_no(int32_t n) @@ -32424,7 +32414,7 @@ We proceed as we did for integers, starting with the array that holds the define @<Hi\TeX\ variables@>= static scaled dimen_defined[0x100]={0}; @ @<Hi\TeX\ variables@>= -static const int hmap_dimen[] ={@t\1\1@>@/ +static const int hmap_dimen[] ={@/ -1, /* |par_indent_code| 0 */ -1, /* |math_surround_code| 1 */ line_skip_limit_no, /* |line_skip_limit_code| 2 */ @@ -32445,7 +32435,7 @@ static const int hmap_dimen[] ={@t\1\1@>@/ hang_indent_no, /* |hang_indent_code| 17 */ -1, /* |h_offset_code| 18 */ -1, /* |v_offset_code| 19 */ - @t\2\2@> emergency_stretch_no /* |emergency_stretch_code| 20 */ + emergency_stretch_no /* |emergency_stretch_code| 20 */ }; @ @<Fix definitions for dimension parameters@>= dimen_defined[zero_dimen_no]=0; @@ -32551,7 +32541,8 @@ glue specifications and often a simple comparison of pointers might suffice. @<Hi\TeX\ variables@>= static pointer glue_defined[0x100]; @ @<Hi\TeX\ variables@>= -static int hmap_glue[] ={@t\1\1@>@/ +static int hmap_glue[] ={ + line_skip_no, /* |line_skip_code| 0 */ baseline_skip_no, /* |baseline_skip_code| 1 */ -1, /* |par_skip_code| 2 */ @@ -32566,7 +32557,7 @@ split_top_skip_no, /* |split_top_skip_code| 10 */ tab_skip_no, /* |tab_skip_code| 11 */ -1, /* |space_skip_code| 12 */ -1, /* |xspace_skip_code| 13 */ -@t\2\2@> par_fill_skip_no /* |par_fill_skip_code| 14 */ +par_fill_skip_no /* |par_fill_skip_code| 14 */ }; @ @<Fix definitions for glue parameters@>= glue_defined[zero_skip_no]=zero_glue; incr(glue_ref_count(zero_glue)); @@ -32818,8 +32809,8 @@ static bool node_equal(pointer p, pointer q) } static bool list_equal(pointer p, pointer q) -{@+loop@+{ - if (p==q) return true; +{@+while (true) + { if (p==q) return true; if (p==null || q==null) return false; if (!node_equal(p,q)) return false; p=link(p);q=link(q); @@ -34397,7 +34388,7 @@ static const char *option_mfmode="ljfour", *option_dpi_str="600"; extern int option_compress; extern unsigned int debugflags; -static struct option long_options[] = {@t\1\1@>@/ +static struct option long_options[] = {@/ { "help", 0, 0, 0 },@/ { "version", 0, 0, 0 },@/ { "interaction", 1, 0, 0 },@/ @@ -34428,8 +34419,8 @@ static struct option long_options[] = {@t\1\1@>@/ { "hint-debug", 1, 0, 0 },@/ { "hint-debug-help", 0, 0, 0 },@/ #endif -@t\2\2@> { 0, 0, 0, 0 } @/ -}; + { 0, 0, 0, 0 }@+}@+; + @ Parsing the command line options is accomplished with the |parse_options| function which in turn uses the |getopt_long_only| @@ -34438,7 +34429,7 @@ function from the \CEE/ library. This function returns 0 and sets the the end of all options is reached. @<\TeX\ Live functions@>= static void parse_options (int argc, char *argv[]) -{@+ loop @+ { +{@+ while (true) { int option_index; int g = getopt_long_only (argc, argv, "+", long_options, &option_index); if (g==0) @@ -34488,7 +34479,7 @@ the option structure. #include <kpathsea/kpathsea.h> static int argument_is(struct option *opt, char * s) {@+ return STREQ(opt->name, s); @+} -#define ARGUMENT_IS(S) @[argument_is(long_options+option_index,S)@] +#define ARGUMENT_IS(S) argument_is(long_options+option_index,S) @ Now we can handle the first two options: @@ -35111,7 +35102,7 @@ last=first; while (optind < argc) input_add_str(argv[optind++]); loc=first; return (loc < last); -} +@+ } @ @<Forward declarations@>= static int input_command_line(void); @@ -35169,9 +35160,8 @@ static bool b_open_out(byte_file *f) /*open a binary file for output*/ {@+f->f=open_out((char *)name_of_file+1,"wb"); return f->f!=NULL && ferror(f->f)==0;@+ } -@# + #ifdef @!INIT -@+@t}\6\4\4{@> static bool w_open_out(word_file *f) /*open a word file for output*/ {@+f->f=open_out((char *)name_of_file+1,"wb"); return f->f!=NULL && ferror(f->f)==0;@+ @@ -35359,8 +35349,6 @@ static int get_md5_sum(int s, int file); @ The code that follows was taken from the \.{texmfmp.c} file of the \TeX\ Live distribution and slightly modified. -@s md5_byte_t int -@s md5_state_t int @<\TeX\ Live auxiliary functions@>= @@ -35419,17 +35407,17 @@ make_time_str(time_t t, bool utc) static void get_creation_date(void) { make_time_str(start_time,source_date_epoch!=NULL); } -@# -#ifdef WIN32 + /* static structure for file status set by |find_input_file| */ -@+@t}\6{@> +#ifdef WIN32 static struct _stat file_stat; -#define GET_FILE_STAT @[_stat(fname,&file_stat)@] +#define GET_FILE_STAT _stat(fname,&file_stat) #else static struct stat file_stat; -#define GET_FILE_STAT @[stat(fname,&file_stat)@] +#define GET_FILE_STAT stat(fname,&file_stat) #endif + static char* find_input_file(void) { char *fname; int r; diff --git a/Build/source/texk/web2c/luatexdir/ChangeLog b/Build/source/texk/web2c/luatexdir/ChangeLog index 282484e2f84..6885c01f395 100644 --- a/Build/source/texk/web2c/luatexdir/ChangeLog +++ b/Build/source/texk/web2c/luatexdir/ChangeLog @@ -1,3 +1,8 @@ +2024-09-01 Luigi Scarso <luigi.scarso@gmail.com> + * Changed char utf8word[(4 * MAX_WORD_LEN) + 1] = { 0 }; + in char utf8word[(4 * MAX_WORD_LEN) + 1] ; + in function void hnj_hyphenation(halfword head, halfword tail) (thanks to user202729@protonmail.com) + 2024-05-11 Luigi Scarso <luigi.scarso@gmail.com> * allow interactive deletion of \outer token, see https://tug.org/texmfbug/newbug.html#B142outer diff --git a/Build/source/texk/web2c/luatexdir/lang/texlang.c b/Build/source/texk/web2c/luatexdir/lang/texlang.c index bc912de0bb1..a7a741b55cb 100644 --- a/Build/source/texk/web2c/luatexdir/lang/texlang.c +++ b/Build/source/texk/web2c/luatexdir/lang/texlang.c @@ -943,7 +943,7 @@ void hnj_hyphenation(halfword head, halfword tail) int lchar, i; struct tex_language *lang; lang_variables langdata; - char utf8word[(4 * MAX_WORD_LEN) + 1] = { 0 }; + char utf8word[(4 * MAX_WORD_LEN) + 1]; int wordlen = 0; char *hy = utf8word; char *replacement = NULL; diff --git a/Build/source/texk/web2c/luatexdir/luatex_svnversion.h b/Build/source/texk/web2c/luatexdir/luatex_svnversion.h index 5422bb4ad06..add4ee39419 100644 --- a/Build/source/texk/web2c/luatexdir/luatex_svnversion.h +++ b/Build/source/texk/web2c/luatexdir/luatex_svnversion.h @@ -1,4 +1,4 @@ #ifndef luatex_svn_revision_h #define luatex_svn_revision_h -#define luatex_svn_revision 7621 +#define luatex_svn_revision 7624 #endif diff --git a/Build/source/texk/web2c/man/ChangeLog b/Build/source/texk/web2c/man/ChangeLog index f82821f1847..b047bb6abf4 100644 --- a/Build/source/texk/web2c/man/ChangeLog +++ b/Build/source/texk/web2c/man/ChangeLog @@ -1,3 +1,7 @@ +2024-08-12 Andreas Scherer <https://ascherer.github.io> + + * ctwill.man: CWEB 4.12 release. + 2024-04-14 TANAKA Takuji <ttk@t-lab.opal.ne.jp> * Makefile.am: Remove eptex. diff --git a/Build/source/texk/web2c/man/ctwill.man b/Build/source/texk/web2c/man/ctwill.man index 50437193073..a30dcd6a0be 100644 --- a/Build/source/texk/web2c/man/ctwill.man +++ b/Build/source/texk/web2c/man/ctwill.man @@ -1,6 +1,6 @@ .\" Automatically generated by Pandoc .\" -.TH "CTWILL" "1" "March 10, 2024" "Web2c @VERSION@" "General Commands Manual" +.TH "CTWILL" "1" "August 12, 2024" "Web2c @VERSION@" "General Commands Manual" .SH NAME ctwill, ctwill-proofsort, ctwill-refsort, ctwill-twinx \- translate CWEB to TeX with mini-indexes @@ -122,7 +122,7 @@ The present incarnation of \f[B]ctwill\f[R] and its utilities tries hard to be a drop-in replacement for the original package. There are, however, a few differences worth noting: .IP \[bu] 2 -This version is based on the most recent version of CWEB (4.11). +This version is based on the most recent version of CWEB (4.12). .IP \[bu] 2 In TeX\ Live the utility programs are prefixed with \f[B]ctwill\-\f[R] and the macro files with \f[B]ct\f[R] for technical reasons. diff --git a/Build/source/texk/web2c/mft.ch b/Build/source/texk/web2c/mft.ch index 57f82579138..4bb78a0bcf1 100644 --- a/Build/source/texk/web2c/mft.ch +++ b/Build/source/texk/web2c/mft.ch @@ -74,7 +74,7 @@ procedure initialize; var @<Local variables for initialization@>@/ begin @<Set initial values@>@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; var @<Local variables for initialization@>@/ begin @@ -989,7 +989,7 @@ used to provide basic formatting. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 5; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/patgen.ch b/Build/source/texk/web2c/patgen.ch index e6accc88d89..c4c45d12a60 100644 --- a/Build/source/texk/web2c/patgen.ch +++ b/Build/source/texk/web2c/patgen.ch @@ -53,7 +53,7 @@ procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin @@ -97,11 +97,11 @@ error message about what caused the error. % machines, from Johannes Hielscher, 10 Jul 2019 00:00:03 (tex-live list), % and later from Mojca Miklavec, 23 Sep 2019 21:21:42. % It seems hopeless, so went back to the original values (10000000/500000). -% +% % The real solution is to provide a way to allocate the arrays % dynamically, so that the large arrays can be used by those who need % them but other are not affected. -% +% % If the values here are still too big, you can probably get it to work % by adding swap or zram; or write a patch to allocate the arrays dynamically. @x [27] @@ -243,7 +243,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<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; diff --git a/Build/source/texk/web2c/patgen.web b/Build/source/texk/web2c/patgen.web index a34597237ff..1d6a7f40e15 100644 --- a/Build/source/texk/web2c/patgen.web +++ b/Build/source/texk/web2c/patgen.web @@ -7,11 +7,14 @@ % Version 2.3 avoided division by zero - Karl Berry (October, 1996). % Version 2.4 avoided use of uninitialized variables (September, 2009). % A few typos fixed, no version change (April, 2020). +% Sorting order fixed, no version change (September, 2024). % Here is TeX material that gets inserted after \input webmac \def\hang{\hangindent 3em\indent\ignorespaces} \def\PASCAL{Pascal} +\def\({} % this is used to make section names sort themselves better + \def\title{PATGEN} \def\contentspagenumber{45} % should be odd \def\topofcontents{\null @@ -722,7 +725,7 @@ right of the occupied region (starting at |trie_max+1|). @p function first_fit: trie_pointer; label found, not_found; var s, @!t: trie_pointer; @!q: q_index; -begin @<Set |s| to the trie base location at which this state should be +begin @<Set \(|s| to the trie base location at which this state should be packed@>; for q:=1 to qmax do {pack it} begin t:=s+q_char(q);@/ @@ -743,7 +746,7 @@ one level of patterns is being generated, the threshold is set to 7 on subsequent levels because the pattern trie will be sparser after bad patterns are deleted (see |delete_bad_patterns|). -@<Set |s| to the trie base location at which this state should be packed@>= +@<Set \(|s| to the trie base location at which this state should be packed@>= if qmax>qmax_thresh then t:=trie_back(trie_max+1) @+else t:=0; loop begin t:=trie_link(t); s:=t-q_char(1); {get next unoccupied cell} @<Ensure |trie| linked up to |s+num_ASCII_codes|@>; @@ -914,7 +917,7 @@ end; @p function firstc_fit: triec_pointer; label found, not_found; var a, @!b: triec_pointer; @!q: q_index; -begin @<Set |b| to the count trie base location at which this state should +begin @<Set \(|b| to the count trie base location at which this state should be packed@>; for q:=1 to qmax do {pack it} begin a:=b+q_char(q);@/ @@ -932,7 +935,7 @@ end; @ The threshold for attempting a first-fit packing is 3 transitions, which is lower than for the pattern trie because speed is more important here. -@<Set |b| to the count trie base location...@>= +@<Set \(|b| to the count trie base location...@>= if qmax>3 then a:=triec_back(triec_max+1) @+else a:=0; loop begin a:=triec_link(a); b:=a-q_char(1);@/ @<Ensure |triec| linked up to |b+num_ASCII_codes|@>; diff --git a/Build/source/texk/web2c/pdftexdir/ChangeLog b/Build/source/texk/web2c/pdftexdir/ChangeLog index 9fcf5a277d2..ee5e17bb9f3 100644 --- a/Build/source/texk/web2c/pdftexdir/ChangeLog +++ b/Build/source/texk/web2c/pdftexdir/ChangeLog @@ -1,3 +1,11 @@ +2024-08-04 Thanh Han The <hanthethanh@gmail.com> + + * pdftoepdf.cc (write_epdf): reversed sep and pdfkeyprefix. + +2024-07-26 Andreas Scherer <https://ascherer.github.io> + + * pdftex.ch: Prepare for compilation with gcc-14. + 2024-05-24 Thanh Han The <hanthethanh@gmail.com> * pdftex.web (pdf_ptex_use_underscore_code), @@ -5,7 +13,7 @@ (Put each ...): define it. (print_param): print it. (get_ptex_use_underscore): new fn to implement it. - (pdf_print_info): adjust Fullbanner prefix character. + (pdf_print_info): adjust Fullbanner prefix character. * pdftoepdf.cc (getptexuseunderscore): declare fn. (write_epdf): use it for FileName, PageNumber, InfoDict. diff --git a/Build/source/texk/web2c/pdftexdir/pdftex.ch b/Build/source/texk/web2c/pdftexdir/pdftex.ch index 0607f7f2f1f..a30a74c3376 100644 --- a/Build/source/texk/web2c/pdftexdir/pdftex.ch +++ b/Build/source/texk/web2c/pdftexdir/pdftex.ch @@ -179,7 +179,7 @@ pdf_font_type:=xmalloc_array(eight_bits, font_max); pdf_font_attr:=xmalloc_array(str_number, font_max); pdf_font_blink:=xmalloc_array(internal_font_number, font_max); pdf_font_elink:=xmalloc_array(internal_font_number, font_max); -pdf_font_has_space_char:=xmalloc_array(internal_font_number, font_max); +pdf_font_has_space_char:=xmalloc_array(boolean, font_max); pdf_font_stretch:=xmalloc_array(integer, font_max); pdf_font_shrink:=xmalloc_array(integer, font_max); pdf_font_step:=xmalloc_array(integer, font_max); @@ -298,7 +298,7 @@ pdf_font_type:=xmalloc_array(eight_bits,font_max); pdf_font_attr:=xmalloc_array(str_number,font_max); pdf_font_blink:=xmalloc_array(internal_font_number,font_max); pdf_font_elink:=xmalloc_array(internal_font_number,font_max); -pdf_font_has_space_char:=xmalloc_array(internal_font_number,font_max); +pdf_font_has_space_char:=xmalloc_array(boolean,font_max); pdf_font_stretch:=xmalloc_array(integer,font_max); pdf_font_shrink:=xmalloc_array(integer,font_max); pdf_font_step:=xmalloc_array(integer,font_max); diff --git a/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc b/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc index 238ed4dba5d..237bd6530c4 100644 --- a/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc +++ b/Build/source/texk/web2c/pdftexdir/pdftoepdf.cc @@ -846,18 +846,18 @@ void write_epdf(void) // write additional information if ((suppress_ptex_info & MASK_SUPPRESS_PTEX_FILENAME) == 0) { - pdf_printf("/%s%sFileName (%s)\n", sep, pdfkeyprefix, + pdf_printf("/%s%sFileName (%s)\n", pdfkeyprefix, sep, convertStringToPDFString(pdf_doc->file_name, strlen(pdf_doc->file_name))); } if ((suppress_ptex_info & MASK_SUPPRESS_PTEX_PAGENUMBER) == 0) { - pdf_printf("/%s%sPageNumber %i\n", sep, pdfkeyprefix, (int) epdf_selected_page); + pdf_printf("/%s%sPageNumber %i\n", pdfkeyprefix, sep, (int) epdf_selected_page); } if ((suppress_ptex_info & MASK_SUPPRESS_PTEX_INFODICT) == 0) { pdf_doc->doc->getDocInfoNF(&info); if (info.isRef()) { // the info dict must be indirect (PDF Ref p. 61) - pdf_printf("/%s%sInfoDict ", sep, pdfkeyprefix); + pdf_printf("/%s%sInfoDict ", pdfkeyprefix, sep); pdf_printf("%d 0 R\n", addOther(info.getRef())); } } diff --git a/Build/source/texk/web2c/pktogf.ch b/Build/source/texk/web2c/pktogf.ch index bd468f2a9de..7f6fc2d80a2 100644 --- a/Build/source/texk/web2c/pktogf.ch +++ b/Build/source/texk/web2c/pktogf.ch @@ -12,7 +12,7 @@ % gf and pk formats. PKtoGF runs silently unless it is given the % -v switch in the command line. -@x [0] l.21 +@x [0] l.22 \def\title{PKtoGF} @y \def\title{PK$\,$\lowercase{to}$\,$GF changes for C} @@ -60,7 +60,7 @@ procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} begin @@ -79,7 +79,7 @@ begin @<Labels...@>=final_end; @y @ This module is deleted, because it is only useful for -a non-local goto, which we don't use in C. +a non-local |goto|\unskip, which we don't use in C. @z % [6] Remove terminal_line_length, since there is no dialog, and @@ -459,7 +459,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 3; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/pktogf.web b/Build/source/texk/web2c/pktogf.web index eadd46c093b..abe9a23fd8b 100644 --- a/Build/source/texk/web2c/pktogf.web +++ b/Build/source/texk/web2c/pktogf.web @@ -8,6 +8,7 @@ % Cleaned up description (bitweight errors) no version change: July 1990 % Fixed bug with empty character setting min_n to 1 (1.1): 19 October 1990 % A few typos, no version change: 22 April 2020, 27 October 2020 +% Sorting order fixed, no version change: 02 September 2024 \def\versiondate{22 April 2020} % \font\ninerm=cmr9 @@ -16,7 +17,7 @@ \def\MF{{\logo META}\-{\logo FONT}} \def\PASCAL{{\mc Pascal}} \def\tamu{Texas A\char38 M} -\def\(#1){} % this is used to make section names sort themselves better +\def\({} % this is used to make section names sort themselves better \def\9#1{} % this is used for sort keys in the index \def\title{PKtoGF} \def\contentspagenumber{1} @@ -1197,7 +1198,7 @@ else @<Read short character preamble@> ; @<Calculate and check |min_m|, |max_m|, |min_n|, and |max_n|@> ; @<Save character locator@> ; -@<Write character preamble@> ; +@<Write \(character preamble@> ; @<Read and translate raster description@> ; gf_byte(eoc) ; last_eoc := gf_loc ; @@ -1360,7 +1361,7 @@ for i := 0 to 255 do @ This module takes care of the simple job of writing the character preamble, after picking one to fit. -@<Write character preamble@>= +@<Write \(character preamble@>= begin if (char_pointer[car mod 256] = -1) and (car >= 0) and (car < 256) and @@ -1408,7 +1409,7 @@ end @ And another module to write out those character locators we have so carefully saved up the information for. -@<Write character locators@>= +@<Write \(character locators@>= for i := 0 to 255 do if char_pointer[i] <> -1 then begin if (s_ver_esc[i] = 0) and (s_hor_esc[i] >= 0) and @@ -1615,7 +1616,7 @@ gf_quad(mmin_m) ; gf_quad(mmax_m) ; gf_quad(mmin_n) ; gf_quad(mmax_n) ; -@<Write character locators@> ; +@<Write \(character locators@> ; gf_byte(post_post) ; gf_quad(j) ; gf_byte(gf_id_byte) ; diff --git a/Build/source/texk/web2c/pktype.ch b/Build/source/texk/web2c/pktype.ch index b5e64566529..58316843997 100644 --- a/Build/source/texk/web2c/pktype.ch +++ b/Build/source/texk/web2c/pktype.ch @@ -8,6 +8,12 @@ % There is no terminal input to this program. % Output is to stdout, and may, of course, be redirected. +@x [0] l.25 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.27 \def\title{PKtype} @y @@ -46,7 +52,7 @@ procedure initialize; {this procedure gets things started properly} @y type @<Types in the outer block@>@/ var @<Globals in the outer block@>@/ -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var i:integer; {loop index for initializations} begin @@ -65,7 +71,7 @@ begin @<Labels...@>=final_end; @y @ This module is deleted, because it is only useful for -a non-local |goto|, which we don't use in C. +a non-local |goto|\unskip, which we don't use in C. @z % [6] No need for |name_length| or |terminal_line_length|. Since these @@ -89,7 +95,7 @@ to the very end of the program with an error message. begin goto final_end; end; @y -@ We use a call to the external C exit to avoid a non-local |goto|. +@ We use a call to the external C exit to avoid a non-local |goto|\unskip. @d abort(#)==begin print_ln(#); uexit(1) end @@ -335,7 +341,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<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; diff --git a/Build/source/texk/web2c/pltotf.ch b/Build/source/texk/web2c/pltotf.ch index fcc08fbc5cb..c4a9a535d24 100644 --- a/Build/source/texk/web2c/pltotf.ch +++ b/Build/source/texk/web2c/pltotf.ch @@ -11,6 +11,12 @@ % 01/16/90 (SR) Version 3.2. % (more recent changes in the ChangeLog) +@x [0] l.31 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.34 \def\title{PL\lowercase{to}TF} @y @@ -45,7 +51,7 @@ procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin kpse_set_program_name (argv[0], my_name); @@ -263,7 +269,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 3; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/pooltype.ch b/Build/source/texk/web2c/pooltype.ch index 74b4d9a2354..8aa11064223 100644 --- a/Build/source/texk/web2c/pooltype.ch +++ b/Build/source/texk/web2c/pooltype.ch @@ -4,6 +4,12 @@ % 11/29/89 (KB) Version released with 8-bit TeX. % (more recent changes in the ChangeLog) +@x [0] l.13 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.16 \def\title{POOL\lowercase{type}} @y @@ -34,7 +40,7 @@ procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@; begin @<Set initial values of key variables@>@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@; begin @@ -112,7 +118,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<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; diff --git a/Build/source/texk/web2c/ptexdir/ChangeLog b/Build/source/texk/web2c/ptexdir/ChangeLog index 2fe03aef772..92cce7d5f48 100644 --- a/Build/source/texk/web2c/ptexdir/ChangeLog +++ b/Build/source/texk/web2c/ptexdir/ChangeLog @@ -1,3 +1,29 @@ +2024-09-22 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * ptex-base.ch, ptex_version.h: Version p4.1.2. + +2024-09-21 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * ptex-base.ch: + Fix a bug in initialization of inhibit_xsp/kinsoku tables + (Bug introduced on commit 2022-12-12 r65248). + * ptex-ctrlsym.test, tests/{ptexlineendmode,ucs}.tex, + (chartoken,ucs-{e,s}}.log, p-lineenda.typ: + Add more regression tests. + * tests/{free_{ixsp,pena},inhibitglue,kinsoku_{search,table}, + postpena}.tex: Move to ../euptexdir/tests . + * am/ptex.am: Adjust. + +2024-09-16 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * ptex-base.ch: Use constants enc_jis and enc_ucs. + * ptex-{ctrlsym,prntknj,ucs}.test, + tests/{control-symbol,ctrlsym,endlinechar,printkanji,ucs}.tex, + tests/{ctrlsym,endline,jctrlsym,pknj-{e,s},ucs-{e,s}).log, + tests/p-{endline,jctrlsym,pknj-{e,s}}a.typ: + Add regression tests by using Kitagawa-san's sample files. + * am/ptex.am: Adjust. + 2024-05-18 Hironori Kitagawa <h_kitagawa2001@yahoo.co.jp> * ptex-base.ch: Enable to get character codes diff --git a/Build/source/texk/web2c/ptexdir/am/ptex.am b/Build/source/texk/web2c/ptexdir/am/ptex.am index ee8117c6800..caf30c2f986 100644 --- a/Build/source/texk/web2c/ptexdir/am/ptex.am +++ b/Build/source/texk/web2c/ptexdir/am/ptex.am @@ -107,9 +107,14 @@ EXTRA_DIST += \ ptex_tests = \ ptexdir/ptriptest.test \ ptexdir/pver.test \ - ptexdir/wcfname.test + ptexdir/wcfname.test \ + ptexdir/ptex-ctrlsym.test \ + ptexdir/ptex-prntknj.test \ + ptexdir/ptex-ucs.test ptexdir/ptriptest.log: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) -ptexdir/pver.log ptexdir/wcfname.log: ptex$(EXEEXT) +ptexdir/pver.log ptexdir/wcfname.log \ + ptexdir/ptex-ctrlsym.log ptexdir/ptex-prntknj.log \ + ptexdir/ptex-ucs.log: ptex$(EXEEXT) EXTRA_DIST += $(ptex_tests) $(pweb_tests) DISTCLEANFILES += $(pweb_programs:=.c) $(pweb_programs:=.h) \ @@ -166,12 +171,31 @@ EXTRA_DIST += ptexdir/tests/skipjfmp.pl ptexdir/tests/skipjfmp.tfm DISTCLEANFILES += ptests/xskipjfmp.* ## ptexdir/ptriptest EXTRA_DIST += ptexdir/ptrip/ptrip.diffs ptexdir/ptrip/texmf.cnf +DISTCLEANFILES += ptrip.diffs + +## ptex-ctrlsym.test +EXTRA_DIST += ptexdir/tests/ctrlsym.tex ptexdir/tests/ctrlsym.log \ + ptexdir/tests/endlinechar.tex ptexdir/tests/endline.log \ + ptexdir/tests/control-symbol.tex ptexdir/tests/jctrlsym.log \ + ptexdir/tests/p-endlinea.typ ptexdir/tests/p-jctrlsyma.typ \ + ptexdir/tests/let_char_token.tex ptexdir/tests/chartoken.log \ + ptexdir/tests/ptexlineendmode.tex ptexdir/tests/p-lineenda.typ +DISTCLEANFILES += p-ctrlsym.log p-ctrlsym.out p-ctrlsym.tex \ + p-endline*.* p-jctrlsym*.* p-chartoken.* p-lineend.* +## ptex-prntknj.test +EXTRA_DIST += ptexdir/tests/printkanji.tex \ + ptexdir/tests/pknj-s.log ptexdir/tests/pknj-e.log \ + ptexdir/tests/p-pknj-sa.typ ptexdir/tests/p-pknj-ea.typ +DISTCLEANFILES += p-pknj-*.* +## ptex-ucs.test +EXTRA_DIST += ptexdir/tests/ucs.tex ptexdir/tests/ucs-s.log \ + ptexdir/tests/ucs-e.log +DISTCLEANFILES += p-ucs*.* + ptrip.diffs: ptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) $(triptrap_diffs) $@ .PHONY: ptrip-clean ptrip-clean: rm -rf ptripdir -DISTCLEANFILES += ptrip.diffs - # (end of ptex.am) diff --git a/Build/source/texk/web2c/ptexdir/ptex-base.ch b/Build/source/texk/web2c/ptexdir/ptex-base.ch index 6b77387d58e..36496b2e4b2 100644 --- a/Build/source/texk/web2c/ptexdir/ptex-base.ch +++ b/Build/source/texk/web2c/ptexdir/ptex-base.ch @@ -74,6 +74,8 @@ % (2022-10-24) HY pTeX p4.1.0 Add new syntax \font [in jis/ucs]. % New primitives: \tojis, \ptextracingfonts and \ptexfontname. % (2023-09-17) HY pTeX p4.1.1 Support more than 256 different glue/kern. +% (2024-09-22) HK pTeX p4.1.2 Enable to get character codes +% from control symbols by using backquote. @x % Here is TeX material that gets inserted after \input webmac @@ -88,8 +90,8 @@ @y @d pTeX_version=4 @d pTeX_minor_version=1 -@d pTeX_revision==".1" -@d pTeX_version_string=='-p4.1.1' {current \pTeX\ version} +@d pTeX_revision==".2" +@d pTeX_version_string=='-p4.1.2' {current \pTeX\ version} @# @d pTeX_banner=='This is pTeX, Version 3.141592653',pTeX_version_string @d pTeX_banner_k==pTeX_banner @@ -1305,10 +1307,11 @@ for k:=0 to 255 do begin cat_code(k):=other_char; kcat_code(k):=other_kchar; math_code(k):=hi(k); sf_code(k):=1000; auto_xsp_code(k):=0; + inhibit_xsp_type(k):=0; kinsoku_type(k):=0; end; for k:=0 to 1023 do - begin inhibit_xsp_code(k):=0; inhibit_xsp_type(k):=0; - kinsoku_code(k):=0; kinsoku_type(k):=0; + begin inhibit_xsp_code(k):=0; + kinsoku_code(k):=0; end; @z @x [17.232] l.4971 - pTeX: initialize cat_code, cur_jfont, cur_tfont @@ -1523,6 +1526,9 @@ primitive("ptextracingfonts",assign_int,int_base+ptex_tracing_fonts_code);@/ @d v_offset==dimen_par(v_offset_code) @d t_baseline_shift==dimen_par(t_baseline_shift_code) @d y_baseline_shift==dimen_par(y_baseline_shift_code) +@# +@d enc_jis=1 +@d enc_ucs=2 @z @x l.5542 - pTeX: @@ -1660,8 +1666,8 @@ procedure print_font_dir_and_enc(f:internal_font_number); begin if font_dir[f]=dir_tate then print("/TATE") else if font_dir[f]=dir_yoko then print("/YOKO"); - if font_enc[f]=2 then print("+Unicode") - else if font_enc[f]=1 then print("+JIS"); + if font_enc[f]=enc_ucs then print("+Unicode") + else if font_enc[f]=enc_jis then print("+JIS"); end; @z @@ -3324,9 +3330,9 @@ if jfm_flag<>dir_default then for k:=ctype_base[f] to ctype_base[f]+nt-1 do begin fget; read_twentyfourx(cx); - if jfm_enc=2 then {Unicode TFM} + if jfm_enc=enc_ucs then {Unicode TFM} font_info[k].hh.rh:=toDVI(fromUCS(cx)) - else if jfm_enc=1 then {JIS-encoded TFM} + else if jfm_enc=enc_jis then {JIS-encoded TFM} font_info[k].hh.rh:=toDVI(fromJIS(cx)) else font_info[k].hh.rh:=tokanji(cx); {|kchar_code|} @@ -3584,9 +3590,9 @@ continue: end; p:=link(p); jc:=KANJI(info(p)); - if font_enc[f]=2 then {Unicode TFM} + if font_enc[f]=enc_ucs then {Unicode TFM} jc:=toUCS(jc) - else if font_enc[f]=1 then {JIS-encoded TFM} + else if font_enc[f]=enc_jis then {JIS-encoded TFM} begin if toJIS(jc)=0 then char_warning_jis(f,jc); jc:=toJIS(jc); end else @@ -7075,8 +7081,8 @@ exit:end; @ @<Scan the font encoding specification@>= begin jfm_enc:=0; if scan_keyword_noexpand("in") then - if scan_keyword_noexpand("jis") then jfm_enc:=1 - else if scan_keyword_noexpand("ucs") then jfm_enc:=2 + if scan_keyword_noexpand("jis") then jfm_enc:=enc_jis + else if scan_keyword_noexpand("ucs") then jfm_enc:=enc_ucs else begin print_err("Unknown TFM encoding"); @.Unknown TFM encoding@> diff --git a/Build/source/texk/web2c/ptexdir/ptex-ctrlsym.test b/Build/source/texk/web2c/ptexdir/ptex-ctrlsym.test new file mode 100755 index 00000000000..ee0c5b88e8a --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/ptex-ctrlsym.test @@ -0,0 +1,77 @@ +#! /bin/sh -vx +# Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +# You may freely use, modify and/or distribute this file. + +BinDir=${BinDir:-.} +ExeExt=${ExeExt:-} +_ptex=$BinDir/ptex$ExeExt +_updvitype=$BinDir/updvitype$ExeExt + +LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE + +TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF +TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFONTS=".;$srcdir//tests"; export TEXFONTS +TEXFORMATS=.; export TEXFORMATS + +rm -f p-ctrlsym.tex p-endline*.* p-jctrlsym*.* p-chartoken.* p-lineend.* + +if test ! -f ./p-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./p-basic.tex +fi +if test ! -f ./p-basic-euc.fmt; then + $_ptex -ini -kanji-internal=euc -jobname=p-basic-euc p-basic || exit 2 +fi + +# ctrlsym.tex +$LN_S $srcdir/ptexdir/tests/ctrlsym.tex ./p-ctrlsym.tex +$_ptex -fmt=p-basic-euc p-ctrlsym || exit 2 + +sed 1d p-ctrlsym.log >p-ctrlsym.out + +diff $srcdir/ptexdir/tests/ctrlsym.log p-ctrlsym.out || exit 3 + + +# endlinchar.tex +$LN_S $srcdir/ptexdir/tests/endlinechar.tex ./p-endline.tex +$_ptex -fmt=p-basic-euc -interaction=batchmode p-endline.tex || : + +sed 1d p-endline.log >p-endline.out + +diff $srcdir/ptexdir/tests/endline.log p-endline.out || exit 4 + +$_updvitype ./p-endline.dvi >ptests/xp-endline.typ +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-endline.typ >ptests/xp-endlinea.typ +diff $srcdir/ptexdir/tests/p-endlinea.typ ptests/xp-endlinea.typ || exit 5 + + +# control-symbol.tex +$LN_S $srcdir/ptexdir/tests/control-symbol.tex ./p-jctrlsym.tex +$_ptex -fmt=p-basic-euc -interaction=batchmode p-jctrlsym.tex || : + +sed 1d p-jctrlsym.log >p-jctrlsym.out + +diff $srcdir/ptexdir/tests/jctrlsym.log p-jctrlsym.out || exit 6 + +$_updvitype ./p-jctrlsym.dvi >ptests/xp-jctrlsym.typ +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-jctrlsym.typ >ptests/xp-jctrlsyma.typ +diff $srcdir/ptexdir/tests/p-jctrlsyma.typ ptests/xp-jctrlsyma.typ || exit 7 + + +# let_char_token.tex +$LN_S $srcdir/ptexdir/tests/let_char_token.tex ./p-chartoken.tex +$_ptex -fmt=p-basic-euc -interaction=batchmode p-chartoken.tex || : + +sed 1d p-chartoken.log >p-chartoken.out + +diff $srcdir/ptexdir/tests/chartoken.log p-chartoken.out || exit 8 + + +# ptexlineendmode.tex +$LN_S $srcdir/ptexdir/tests/ptexlineendmode.tex ./p-lineend.tex +$_ptex -fmt=p-basic-euc -kanji=utf8 -interaction=batchmode p-lineend.tex || : + +$_updvitype -kanji=utf8 ./p-lineend.dvi >ptests/xp-lineend.typ +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-lineend.typ >ptests/xp-lineenda.typ +diff $srcdir/ptexdir/tests/p-lineenda.typ ptests/xp-lineenda.typ || exit 9 + diff --git a/Build/source/texk/web2c/ptexdir/ptex-prntknj.test b/Build/source/texk/web2c/ptexdir/ptex-prntknj.test new file mode 100755 index 00000000000..0f2c50f96fd --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/ptex-prntknj.test @@ -0,0 +1,48 @@ +#! /bin/sh -vx +# Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +# You may freely use, modify and/or distribute this file. + +BinDir=${BinDir:-.} +ExeExt=${ExeExt:-} +_ptex=$BinDir/ptex$ExeExt +_updvitype=$BinDir/updvitype$ExeExt + +LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE + +TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF +OFMFONTS=".;$srcdir/tests"; export OFMFONTS +TFMFONTS=".;$srcdir//tests"; export TFMFONTS +TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFORMATS=.; export TEXFORMATS + +rm -f p-base.* p-ucs*.* +$LN_S $srcdir/ptexdir/tests/printkanji.tex ./printkanji.tex + +if test ! -f ./p-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./p-basic.tex +fi +if test ! -f ./p-basic-sjis.fmt; then + $_ptex -ini -kanji-internal=sjis -jobname=p-basic-sjis p-basic || exit 1 +fi +if test ! -f ./p-basic-euc.fmt; then + $_ptex -ini -kanji-internal=euc -jobname=p-basic-euc p-basic || exit 2 +fi + +$_ptex -fmt=p-basic-sjis -kanji-internal=sjis -jobname=p-pknj-s -interaction=batchmode printkanji || : +$_ptex -fmt=p-basic-euc -kanji-internal=euc -jobname=p-pknj-e -interaction=batchmode printkanji || : + +sed 1d p-pknj-s.log >p-pknj-s.out +sed 1d p-pknj-e.log >p-pknj-e.out + +diff $srcdir/ptexdir/tests/pknj-s.log p-pknj-s.out || exit 10 +diff $srcdir/ptexdir/tests/pknj-e.log p-pknj-e.out || exit 11 + +$_updvitype ./p-pknj-s.dvi >ptests/xp-pknj-s.typ +$_updvitype ./p-pknj-e.dvi >ptests/xp-pknj-e.typ + +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-pknj-s.typ >ptests/xp-pknj-sa.typ +sed "1d;s/TeX output 20[23].\...\...:.*'/TeX output 20XX.XX.XX'/" ptests/xp-pknj-e.typ >ptests/xp-pknj-ea.typ + +diff $srcdir/ptexdir/tests/p-pknj-sa.typ ptests/xp-pknj-sa.typ || exit 20 +diff $srcdir/ptexdir/tests/p-pknj-ea.typ ptests/xp-pknj-ea.typ || exit 21 + diff --git a/Build/source/texk/web2c/ptexdir/ptex-ucs.test b/Build/source/texk/web2c/ptexdir/ptex-ucs.test new file mode 100755 index 00000000000..bef8ba44910 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/ptex-ucs.test @@ -0,0 +1,37 @@ +#! /bin/sh -vx +# Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +# You may freely use, modify and/or distribute this file. + +BinDir=${BinDir:-.} +ExeExt=${ExeExt:-} +_ptex=$BinDir/ptex$ExeExt + +LC_ALL=C; export LC_ALL; LANGUAGE=C; export LANGUAGE + +TEXMFCNF=$srcdir/../kpathsea;export TEXMFCNF +OFMFONTS=".;$srcdir/tests"; export OFMFONTS +TEXINPUTS=".;$srcdir/tests"; export TEXINPUTS +TEXFORMATS=.; export TEXFORMATS + +rm -f p-ucs*.* +$LN_S $srcdir/ptexdir/tests/ucs.tex ./p-ucs.tex + +if test ! -f ./p-basic.tex; then + cp $srcdir/tests/dump-basic.tex ./p-basic.tex +fi +if test ! -f ./p-basic-sjis.fmt; then + $_ptex -ini -kanji-internal=sjis -jobname=p-basic-sjis p-basic || exit 1 +fi +if test ! -f ./p-basic-euc.fmt; then + $_ptex -ini -kanji-internal=euc -jobname=p-basic-euc p-basic || exit 2 +fi + +$_ptex -fmt=p-basic-sjis -kanji-internal=sjis -jobname=p-ucs-s -interaction=batchmode p-ucs || exit 4 +$_ptex -fmt=p-basic-euc -kanji-internal=euc -jobname=p-ucs-e -interaction=batchmode p-ucs || exit 5 + +sed 1d p-ucs-s.log >p-ucs-s.out +sed 1d p-ucs-e.log >p-ucs-e.out + +diff $srcdir/ptexdir/tests/ucs-s.log p-ucs-s.out || exit 10 +diff $srcdir/ptexdir/tests/ucs-e.log p-ucs-e.out || exit 11 + diff --git a/Build/source/texk/web2c/ptexdir/ptex_version.h b/Build/source/texk/web2c/ptexdir/ptex_version.h index 13e0f1467b3..247aabb113d 100644 --- a/Build/source/texk/web2c/ptexdir/ptex_version.h +++ b/Build/source/texk/web2c/ptexdir/ptex_version.h @@ -1 +1 @@ -#define PTEX_VERSION "p4.1.1" +#define PTEX_VERSION "p4.1.2" diff --git a/Build/source/texk/web2c/ptexdir/tests/chartoken.log b/Build/source/texk/web2c/ptexdir/tests/chartoken.log new file mode 100644 index 00000000000..e834c9cf8a3 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/chartoken.log @@ -0,0 +1,41 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**p-chartoken.tex +(./p-chartoken.tex +\x=\count26 + +! Improper alphabetic or KANJI constant. +<to be read again> + \aaa +\T #1->\x =`#1 + \message {\the \x } +l.6 \def\aaa{西}\T{\aaa} + % err +A one-character control sequence belongs after a ` mark. +So I'm essentially inserting \0 here. + +48 +! Improper alphabetic or KANJI constant. +<to be read again> + \AAA +\T #1->\x =`#1 + \message {\the \x } +l.7 \def\AAA{s}\T{\AAA} + % err +A one-character control sequence belongs after a ` mark. +So I'm essentially inserting \0 here. + +Missing character: There is no s in font nullfont! +48 +! Improper alphabetic or KANJI constant. +<to be read again> + \ccc +\T #1->\x =`#1 + \message {\the \x } +l.8 \let\ccc=西\relax\T{\ccc} + % err +A one-character control sequence belongs after a ` mark. +So I'm essentially inserting \0 here. + +48 126 97 46273 46273 [1] ) +Output written on p-chartoken.dvi (1 page, 152 bytes). diff --git a/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex b/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex index f8127546885..be05906602d 100644 --- a/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex +++ b/Build/source/texk/web2c/ptexdir/tests/control-symbol.tex @@ -1,5 +1,14 @@ \scrollmode % plain TeX ソース +\font\cmr=cmr10\cmr +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi + \def\+{A} \def\+{A} \def\X{\+} @@ -95,7 +104,5 @@ 4. \】 ◇, \@ ◇ - \end - diff --git a/Build/source/texk/web2c/ptexdir/tests/ctrlsym.log b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.log new file mode 100644 index 00000000000..a74fd6faac3 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.log @@ -0,0 +1,11 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**p-ctrlsym +(./p-ctrlsym.tex +A: 49379 + +\雪 ->snow +B: 49379. 雪\雪snow +macro:->snow. + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex new file mode 100644 index 00000000000..d84a541707b --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/ctrlsym.tex @@ -0,0 +1,36 @@ +% Copyright 2024 Japanese TeX Development Community <issue@texjp.org> +% You may freely use, modify and/or distribute this file. +% +\ifx\fmtname\undefined + \input basic + \def\fmtname{basic} + \expandafter\dump +\fi +%================== +\begingroup % Just there to minimize tracing output + \tracingmacros=2 + \tracingonline=1 +%-- + \newlinechar=10 + \count255=0 + \count255=`雪 \relax + \message{^^JA: \the\count255^^J} + \def\雪{snow} + \count255=`\雪 \relax + \message{B: \the\count255. 雪\string\雪 \雪^^J} + \message{\expandafter\meaning\csname 雪\endcsname.^^J} + \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"96EA\endcsname.^^J}\fi% + \ifnum\jis"2121="3000 + \count4095=0 + \count4095=`🍌 \relax + \message{^^JC: \the\count4095^^J} + \def\🍌{banana} + \count4095=`\🍌 \relax + \message{D: \the\count4095. 🍌\string\🍌 \🍌^^J} + \message{\expandafter\meaning\csname 🍌\endcsname.^^J} + \ifx\Uchar\undefined\else\message{\expandafter\meaning\csname \Uchar"1F34C\endcsname.^^J}\fi% + \fi +%-- + \setbox0=\vbox{} % no dvi/xdv/pdf output +\endgroup +\end diff --git a/Build/source/texk/web2c/ptexdir/tests/endline.log b/Build/source/texk/web2c/ptexdir/tests/endline.log new file mode 100644 index 00000000000..517be160e49 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/endline.log @@ -0,0 +1,24 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**p-endline.tex +(./p-endline.tex +! Undefined control sequence. +l.29 7a\^^e3 + ^^81^^82b\^^e3^^81^^82 +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.29 7a\^^e3^^81^^82b\^^e3 + ^^81^^82 +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +[1] ) +Output written on p-endline.dvi (1 page, 232 bytes). diff --git a/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex b/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex index ef59b1fc886..5dc8de812e7 100644 --- a/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex +++ b/Build/source/texk/web2c/ptexdir/tests/endlinechar.tex @@ -1,23 +1,23 @@ -\font\x=ecrm1000 \x +\font\x=ec-lmr10 \x \parindent0pt\noautoxspacing \output{\shipout\box255} -\def\あ{ア} -\def\,{イ} +\def\あ{PPP} +\def\,{QQQ} 1a\, -% --> 1aイ +% --> 1aQQQ 2a\あ -% --> 2aア +% --> 2aPPP \endlinechar=-1\relax 3a\, -% --> 3aイ +% --> 3aQQQ % previous pTeX, upTeX: Undefined cs. 4a\あ -% --> 4aア +% --> 4aPPP % prev. pTeX: Undefined cs. 5a, @@ -36,18 +36,22 @@ \catcode"81=11\relax \catcode"82=11\relax 8a\^^e3^^81^^82 - % --> 8aア + % (p3.10.0-u1.27) --> 8aPPP + % (p4.0.0-u1.27) --> エラー 8a \kcatcode`あ=15\relax 9a\あ - % == 9a\^^e3^^81^^82. --> 9aア - \def\^^e3^^81^^82{ウ} + % == 9a\^^e3^^81^^82. + % (p3.10.0-u1.27) --> 9aPPP + % (p4.0.0-u1.27) --> エラー 9a + \def\^^e3^^81^^82{RRR} 10a\あ b\あ - % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 10aウbウ + % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 10aRRRbRRR \kcatcode12354=16\relax 11a\あ b\あ - % == 10a\^^e3^^81^^82 b\^^e3^^81^^82. --> 11aウbウ + % == 11a\^^e3^^81^^82 b\^^e3^^81^^82. + % (p3.10.0-u1.27) --> 11aRRRbRRR + % (p4.0.0-u1.27) --> 11aPPPbPPP \fi \bye - diff --git a/Build/source/texk/web2c/ptexdir/tests/jctrlsym.log b/Build/source/texk/web2c/ptexdir/tests/jctrlsym.log new file mode 100644 index 00000000000..a1e190a80df --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/jctrlsym.log @@ -0,0 +1,42 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**p-jctrlsym.tex +(./p-jctrlsym.tex +■control symbol +> \X=macro: +->\+. +l.27 \show\X + \relax\message{(\meaning\X)} + +(macro:->\+) +> \Y=macro: +->\+. +l.28 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\+) ■control word +> \X=macro: +->\+ . +l.39 \show\X + \relax\message{(\meaning\X)} + +(macro:->\+ ) +> \Y=macro: +->\+ . +l.40 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\+ ) ■control word +> \X=macro: +->\ABC . +l.48 \show\X + \relax\message{(\meaning\X)} + +(macro:->\ABC ) +> \Y=macro: +->\あいう . +l.49 \show\Y + \relax\message{(\meaning\Y)} + +(macro:->\あいう ) ■行末の和文コントロールシンボル [1] ) +Output written on p-jctrlsym.dvi (1 page, 376 bytes). diff --git a/Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ b/Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ new file mode 100644 index 00000000000..31b9af3a8c3 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/p-endlinea.typ @@ -0,0 +1,68 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 167. +maxv=42152922, maxh=30785863, maxstackdepth=1, totalpages=1 +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 1 +87: down3 655360 v:=0+655360=655360, vv:=42 +91: push +level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +92: fntdef1 0: ec-lmr10 +116: fntnum0 current font is ec-lmr10 +117: setchar49 ("31) h:=0+327680=327680, hh:=21 +118: setchar97 ("61) h:=327680+327680=655360, hh:=42 +119: setchar81 ("51) h:=655360+509738=1165098, hh:=74 +120: setchar81 ("51) h:=1165098+509738=1674836, hh:=106 +121: setchar81 ("51) h:=1674836+509738=2184574, hh:=138 +[1aQQQ] +122: pop +level 0:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +123: y3 786432 v:=655360+786432=1441792, vv:=91 +127: push +level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +128: setchar50 ("32) h:=0+327680=327680, hh:=21 +129: setchar97 ("61) h:=327680+327680=655360, hh:=42 +130: setchar80 ("50) h:=655360+446005=1101365, hh:=70 +131: setchar80 ("50) h:=1101365+446005=1547370, hh:=98 +132: setchar80 ("50) h:=1547370+446005=1993375, hh:=126 +[2aPPP] +133: pop +level 0:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +134: y0 786432 v:=1441792+786432=2228224, vv:=141 +135: push +level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +136: setchar51 ("33) h:=0+327680=327680, hh:=21 +137: setchar97 ("61) h:=327680+327680=655360, hh:=42 +138: setchar81 ("51) h:=655360+509738=1165098, hh:=74 +139: setchar81 ("51) h:=1165098+509738=1674836, hh:=106 +140: setchar81 ("51) h:=1674836+509738=2184574, hh:=138 +141: setchar52 ("34) h:=2184574+327680=2512254, hh:=159 +142: setchar97 ("61) h:=2512254+327680=2839934, hh:=180 +143: setchar80 ("50) h:=2839934+446005=3285939, hh:=208 +144: setchar80 ("50) h:=3285939+446005=3731944, hh:=236 +145: setchar80 ("50) h:=3731944+446005=4177949, hh:=264 +146: setchar53 ("35) h:=4177949+327680=4505629, hh:=285 +147: setchar97 ("61) h:=4505629+327680=4833309, hh:=306 +148: setchar54 ("36) h:=4833309+327680=5160989, hh:=327 +149: setchar97 ("61) h:=5160989+327680=5488669, hh:=348 +150: setchar55 ("37) h:=5488669+327680=5816349, hh:=369 +151: setchar97 ("61) h:=5816349+327680=6144029, hh:=390 +[3aQQQ4aPPP5a6a7a] +152: set1 129("81) h:=6144029+491520=6635549, hh:=421 +154: w2 -18205 h:=6635549-18205=6617344, hh:=420 +157: set1 130("82) h:=6617344+473301=7090645, hh:=450 +159: setchar98 ("62) h:=7090645+364085=7454730, hh:=473 +[b] +160: set1 129("81) h:=7454730+491520=7946250, hh:=504 +162: w0 -18205 h:=7946250-18205=7928045, hh:=503 +163: set1 130("82) h:=7928045+473301=8401346, hh:=533 +165: pop +level 0:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +166: eop diff --git a/Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ b/Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ new file mode 100644 index 00000000000..7f8a5eb1ca6 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/p-jctrlsyma.typ @@ -0,0 +1,123 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 293. +maxv=43725786, maxh=30785863, maxstackdepth=2, totalpages=1 +Font 1: min10 (JFM)---loaded at size 655360 DVI units +Font 0: cmr10---loaded at size 655360 DVI units + +42: beginning of page 1 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: cmr10 +130: fntnum0 current font is cmr10 +131: setchar49 ("31) h:=1310720+327681=1638401, hh:=104 +132: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +133: right3 291271 h:=1820446+291271=2111717, hh:=134 +[1. ] +137: fntdef1 1: min10 +158: fntnum1 current font is min10 +159: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174 +162: set2 8574("217E) type=0 h:=2742315+630598=3372913, hh:=214 +165: set2 8484("2124) type=8 h:=3372913+231778=3604691, hh:=229 +168: right3 398820 h:=3604691+398820=4003511, hh:=254 +172: set2 8737("2221) type=0 h:=4003511+630598=4634109, hh:=294 +175: set2 8574("217E) type=0 h:=4634109+630598=5264707, hh:=334 +[∡ⅾℤ ∡ⅾ] +178: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +179: y3 786432 v:=655360+786432=1441792, vv:=91 +183: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +184: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +188: fntnum0 current font is cmr10 +189: setchar50 ("32) h:=1310720+327681=1638401, hh:=104 +190: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +191: right3 291271 h:=1820446+291271=2111717, hh:=134 +[2. ] +195: fntnum1 current font is min10 +196: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174 +199: set2 8574("217E) type=0 h:=2742315+630598=3372913, hh:=214 +[∡ⅾ] +202: fntnum0 current font is cmr10 +203: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226 +204: right3 218453 h:=3554958+218453=3773411, hh:=239 +[, ] +208: fntnum1 current font is min10 +209: set2 8737("2221) type=0 h:=3773411+630598=4404009, hh:=279 +212: set2 8574("217E) type=0 h:=4404009+630598=5034607, hh:=319 +[∡ⅾ] +215: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +216: y0 786432 v:=1441792+786432=2228224, vv:=141 +217: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +218: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +222: fntnum0 current font is cmr10 +223: setchar51 ("33) h:=1310720+327681=1638401, hh:=104 +224: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +225: right3 291271 h:=1820446+291271=2111717, hh:=134 +[3. ] +229: fntnum1 current font is min10 +230: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174 +233: set2 8574("217E) type=0 h:=2742315+630598=3372913, hh:=214 +[∡ⅾ] +236: fntnum0 current font is cmr10 +237: setchar44 ("2C) h:=3372913+182045=3554958, hh:=226 +238: w3 218453 h:=3554958+218453=3773411, hh:=239 +[, ] +242: fntnum1 current font is min10 +243: set2 8737("2221) type=0 h:=3773411+630598=4404009, hh:=279 +246: w0 218453 h:=4404009+218453=4622462, hh:=293 +247: set2 8574("217E) type=0 h:=4622462+630598=5253060, hh:=333 +[∡ ⅾ] +250: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +251: y0 786432 v:=2228224+786432=3014656, vv:=191 +252: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +253: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +257: fntnum0 current font is cmr10 +258: setchar52 ("34) h:=1310720+327681=1638401, hh:=104 +259: setchar46 ("2E) h:=1638401+182045=1820446, hh:=116 +260: right3 291271 h:=1820446+291271=2111717, hh:=134 +[4. ] +264: fntnum1 current font is min10 +265: set2 8737("2221) type=0 h:=2111717+630598=2742315, hh:=174 +268: w3 218453 h:=2742315+218453=2960768, hh:=188 +272: set2 8574("217E) type=0 h:=2960768+630598=3591366, hh:=228 +[∡ ⅾ] +275: fntnum0 current font is cmr10 +276: setchar44 ("2C) h:=3591366+182045=3773411, hh:=240 +277: w0 218453 h:=3773411+218453=3991864, hh:=253 +[, ] +278: fntnum1 current font is min10 +279: set2 8737("2221) type=0 h:=3991864+630598=4622462, hh:=293 +282: w0 218453 h:=4622462+218453=4840915, hh:=307 +283: set2 8574("217E) type=0 h:=4840915+630598=5471513, hh:=347 +[∡ ⅾ] +286: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +287: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +288: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +292: eop diff --git a/Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ b/Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ new file mode 100644 index 00000000000..368a57272ae --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/p-lineenda.typ @@ -0,0 +1,295 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 635. +maxv=43725786, maxh=30785863, maxstackdepth=2, totalpages=1 +Font 1: min10 (JFM)---loaded at size 655360 DVI units +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 1 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: ec-lmr10 +133: fntnum0 current font is ec-lmr10 +134: setchar48 ("30) h:=1310720+327680=1638400, hh:=104 +[0] +135: fntdef1 1: min10 +156: fntnum1 current font is min10 +157: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +160: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184 +163: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224 +166: set2 9250("2422) type=0 h:=3530194+630598=4160792, hh:=264 +169: set2 8574("217E) type=0 h:=4160792+630598=4791390, hh:=304 +172: set2 9252("2424) type=0 h:=4791390+630598=5421988, hh:=344 +175: set2 8737("2221) type=0 h:=5421988+630598=6052586, hh:=384 +178: set2 8574("217E) type=0 h:=6052586+630598=6683184, hh:=424 +181: set2 8737("2221) type=0 h:=6683184+630598=7313782, hh:=464 +184: set2 8574("217E) type=0 h:=7313782+630598=7944380, hh:=504 +187: set2 13377("3441) type=0 h:=7944380+630598=8574978, hh:=544 +190: set2 15226("3B7A) type=0 h:=8574978+630598=9205576, hh:=584 +[◆◆◇あ◇い◆◇◆◇漢字] +193: fntnum0 current font is ec-lmr10 +194: setchar83 ("53) h:=9205576+364085=9569661, hh:=607 +195: setchar97 ("61) h:=9569661+327680=9897341, hh:=628 +196: right3 655360 h:=9897341+655360=10552701, hh:=668 +200: setchar112 ("70) h:=10552701+364085=10916786, hh:=691 +201: setchar52 ("34) h:=10916786+327680=11244466, hh:=712 +202: setchar46 ("2E) h:=11244466+182043=11426509, hh:=724 +203: setchar48 ("30) h:=11426509+327680=11754189, hh:=745 +204: setchar46 ("2E) h:=11754189+182043=11936232, hh:=757 +205: setchar48 ("30) h:=11936232+327680=12263912, hh:=778 +[Sa p4.0.0] +206: fntnum1 current font is min10 +207: set2 12362("304A) type=0 h:=12263912+630598=12894510, hh:=818 +210: set2 14687("395F) type=0 h:=12894510+630598=13525108, hh:=858 +[以降] +213: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +214: y3 786432 v:=655360+786432=1441792, vv:=91 +218: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +219: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +223: fntnum0 current font is ec-lmr10 +224: setchar49 ("31) h:=1310720+327680=1638400, hh:=104 +[1] +225: fntnum1 current font is min10 +226: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +229: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184 +232: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224 +235: set2 9250("2422) type=0 h:=3530194+630598=4160792, hh:=264 +238: set2 8574("217E) type=0 h:=4160792+630598=4791390, hh:=304 +241: set2 9252("2424) type=0 h:=4791390+630598=5421988, hh:=344 +244: set2 8737("2221) type=0 h:=5421988+630598=6052586, hh:=384 +247: set2 8574("217E) type=0 h:=6052586+630598=6683184, hh:=424 +250: right3 218453 h:=6683184+218453=6901637, hh:=437 +254: set2 8737("2221) type=0 h:=6901637+630598=7532235, hh:=477 +257: set2 8574("217E) type=0 h:=7532235+630598=8162833, hh:=517 +260: set2 13377("3441) type=0 h:=8162833+630598=8793431, hh:=557 +263: set2 15226("3B7A) type=0 h:=8793431+630598=9424029, hh:=597 +[◆◆◇あ◇い◆◇ ◆◇漢字] +266: fntnum0 current font is ec-lmr10 +267: setchar83 ("53) h:=9424029+364085=9788114, hh:=620 +268: setchar97 ("61) h:=9788114+327680=10115794, hh:=641 +269: right3 655360 h:=10115794+655360=10771154, hh:=682 +273: setchar112 ("70) h:=10771154+364085=11135239, hh:=705 +274: setchar51 ("33) h:=11135239+327680=11462919, hh:=726 +275: setchar46 ("2E) h:=11462919+182043=11644962, hh:=738 +276: setchar56 ("38) h:=11644962+327680=11972642, hh:=759 +277: setchar46 ("2E) h:=11972642+182043=12154685, hh:=771 +278: setchar50 ("32) h:=12154685+327680=12482365, hh:=792 +[Sa p3.8.2] +279: fntnum1 current font is min10 +280: set2 12362("304A) type=0 h:=12482365+630598=13112963, hh:=832 +283: set2 14687("395F) type=0 h:=13112963+630598=13743561, hh:=872 +[以降] +286: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +287: y0 786432 v:=1441792+786432=2228224, vv:=141 +288: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +289: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +293: fntnum0 current font is ec-lmr10 +294: setchar50 ("32) h:=1310720+327680=1638400, hh:=104 +[2] +295: fntnum1 current font is min10 +296: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +299: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184 +302: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224 +305: w3 218453 h:=3530194+218453=3748647, hh:=237 +309: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=277 +312: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=317 +315: w0 218453 h:=5009843+218453=5228296, hh:=331 +316: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371 +319: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411 +322: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451 +325: set2 8737("2221) type=0 h:=7120090+630598=7750688, hh:=491 +328: set2 8574("217E) type=0 h:=7750688+630598=8381286, hh:=531 +331: set2 13377("3441) type=0 h:=8381286+630598=9011884, hh:=571 +334: set2 15226("3B7A) type=0 h:=9011884+630598=9642482, hh:=611 +[◆◆◇ あ◇ い◆◇◆◇漢字] +337: fntnum0 current font is ec-lmr10 +338: setchar83 ("53) h:=9642482+364085=10006567, hh:=634 +339: setchar97 ("61) h:=10006567+327680=10334247, hh:=655 +[Sa] +340: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +341: y0 786432 v:=2228224+786432=3014656, vv:=191 +342: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +343: right3 1310720 h:=0+1310720=1310720, hh:=83 +347: setchar51 ("33) h:=1310720+327680=1638400, hh:=104 +[ 3] +348: fntnum1 current font is min10 +349: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +352: set2 8737("2221) type=0 h:=2268998+630598=2899596, hh:=184 +355: set2 8574("217E) type=0 h:=2899596+630598=3530194, hh:=224 +358: w3 218453 h:=3530194+218453=3748647, hh:=237 +362: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=277 +365: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=317 +368: w0 218453 h:=5009843+218453=5228296, hh:=331 +369: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371 +372: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411 +375: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451 +378: w0 218453 h:=7120090+218453=7338543, hh:=465 +379: set2 8737("2221) type=0 h:=7338543+630598=7969141, hh:=505 +382: set2 8574("217E) type=0 h:=7969141+630598=8599739, hh:=545 +385: set2 13377("3441) type=0 h:=8599739+630598=9230337, hh:=585 +388: set2 15226("3B7A) type=0 h:=9230337+630598=9860935, hh:=625 +[◆◆◇ あ◇ い◆◇ ◆◇漢字] +391: fntnum0 current font is ec-lmr10 +392: setchar83 ("53) h:=9860935+364085=10225020, hh:=648 +393: setchar97 ("61) h:=10225020+327680=10552700, hh:=669 +394: right3 655360 h:=10552700+655360=11208060, hh:=710 +398: setchar112 ("70) h:=11208060+364085=11572145, hh:=733 +399: setchar51 ("33) h:=11572145+327680=11899825, hh:=754 +400: setchar46 ("2E) h:=11899825+182043=12081868, hh:=766 +401: setchar56 ("38) h:=12081868+327680=12409548, hh:=787 +402: setchar46 ("2E) h:=12409548+182043=12591591, hh:=799 +403: setchar49 ("31) h:=12591591+327680=12919271, hh:=820 +[Sa p3.8.1] +404: fntnum1 current font is min10 +405: set2 12362("304A) type=0 h:=12919271+630598=13549869, hh:=860 +408: set2 16688("4130) type=0 h:=13549869+630598=14180467, hh:=900 +[以前] +411: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +412: y0 786432 v:=3014656+786432=3801088, vv:=241 +413: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +414: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +418: fntnum0 current font is ec-lmr10 +419: setchar52 ("34) h:=1310720+327680=1638400, hh:=104 +[4] +420: fntnum1 current font is min10 +421: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +424: w3 218453 h:=2268998+218453=2487451, hh:=158 +428: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198 +431: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238 +434: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=278 +437: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=318 +440: w0 218453 h:=5009843+218453=5228296, hh:=331 +441: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371 +444: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411 +447: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451 +450: set2 8737("2221) type=0 h:=7120090+630598=7750688, hh:=491 +453: set2 8574("217E) type=0 h:=7750688+630598=8381286, hh:=531 +456: set2 13377("3441) type=0 h:=8381286+630598=9011884, hh:=571 +459: set2 15226("3B7A) type=0 h:=9011884+630598=9642482, hh:=611 +[◆ ◆◇あ◇ い◆◇◆◇漢字] +462: fntnum0 current font is ec-lmr10 +463: setchar83 ("53) h:=9642482+364085=10006567, hh:=634 +464: setchar97 ("61) h:=10006567+327680=10334247, hh:=655 +[Sa] +465: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +466: y0 786432 v:=3801088+786432=4587520, vv:=291 +467: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +468: right3 1310720 h:=0+1310720=1310720, hh:=83 +472: setchar53 ("35) h:=1310720+327680=1638400, hh:=104 +[ 5] +473: fntnum1 current font is min10 +474: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +477: w3 218453 h:=2268998+218453=2487451, hh:=158 +481: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198 +484: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238 +487: set2 9250("2422) type=0 h:=3748647+630598=4379245, hh:=278 +490: set2 8574("217E) type=0 h:=4379245+630598=5009843, hh:=318 +493: w0 218453 h:=5009843+218453=5228296, hh:=331 +494: set2 9252("2424) type=0 h:=5228296+630598=5858894, hh:=371 +497: set2 8737("2221) type=0 h:=5858894+630598=6489492, hh:=411 +500: set2 8574("217E) type=0 h:=6489492+630598=7120090, hh:=451 +503: w0 218453 h:=7120090+218453=7338543, hh:=465 +504: set2 8737("2221) type=0 h:=7338543+630598=7969141, hh:=505 +507: set2 8574("217E) type=0 h:=7969141+630598=8599739, hh:=545 +510: set2 13377("3441) type=0 h:=8599739+630598=9230337, hh:=585 +513: set2 15226("3B7A) type=0 h:=9230337+630598=9860935, hh:=625 +[◆ ◆◇あ◇ い◆◇ ◆◇漢字] +516: fntnum0 current font is ec-lmr10 +517: setchar83 ("53) h:=9860935+364085=10225020, hh:=648 +518: setchar97 ("61) h:=10225020+327680=10552700, hh:=669 +[Sa] +519: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +520: y0 786432 v:=4587520+786432=5373952, vv:=340 +521: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +522: right3 1310720 h:=0+1310720=1310720, hh:=83 +526: setchar54 ("36) h:=1310720+327680=1638400, hh:=104 +[ 6] +527: fntnum1 current font is min10 +528: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +531: w3 218453 h:=2268998+218453=2487451, hh:=158 +535: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198 +538: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238 +541: w0 218453 h:=3748647+218453=3967100, hh:=251 +542: set2 9250("2422) type=0 h:=3967100+630598=4597698, hh:=291 +545: set2 8574("217E) type=0 h:=4597698+630598=5228296, hh:=331 +548: w0 218453 h:=5228296+218453=5446749, hh:=345 +549: set2 9252("2424) type=0 h:=5446749+630598=6077347, hh:=385 +552: set2 8737("2221) type=0 h:=6077347+630598=6707945, hh:=425 +555: set2 8574("217E) type=0 h:=6707945+630598=7338543, hh:=465 +558: set2 8737("2221) type=0 h:=7338543+630598=7969141, hh:=505 +561: set2 8574("217E) type=0 h:=7969141+630598=8599739, hh:=545 +564: set2 13377("3441) type=0 h:=8599739+630598=9230337, hh:=585 +567: set2 15226("3B7A) type=0 h:=9230337+630598=9860935, hh:=625 +[◆ ◆◇ あ◇ い◆◇◆◇漢字] +570: fntnum0 current font is ec-lmr10 +571: setchar83 ("53) h:=9860935+364085=10225020, hh:=648 +572: setchar97 ("61) h:=10225020+327680=10552700, hh:=669 +[Sa] +573: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +574: y0 786432 v:=5373952+786432=6160384, vv:=390 +575: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +576: right3 1310720 h:=0+1310720=1310720, hh:=83 +580: setchar55 ("37) h:=1310720+327680=1638400, hh:=104 +[ 7] +581: fntnum1 current font is min10 +582: set2 8737("2221) type=0 h:=1638400+630598=2268998, hh:=144 +585: w3 218453 h:=2268998+218453=2487451, hh:=158 +589: set2 8737("2221) type=0 h:=2487451+630598=3118049, hh:=198 +592: set2 8574("217E) type=0 h:=3118049+630598=3748647, hh:=238 +595: w0 218453 h:=3748647+218453=3967100, hh:=251 +596: set2 9250("2422) type=0 h:=3967100+630598=4597698, hh:=291 +599: set2 8574("217E) type=0 h:=4597698+630598=5228296, hh:=331 +602: w0 218453 h:=5228296+218453=5446749, hh:=345 +603: set2 9252("2424) type=0 h:=5446749+630598=6077347, hh:=385 +606: set2 8737("2221) type=0 h:=6077347+630598=6707945, hh:=425 +609: set2 8574("217E) type=0 h:=6707945+630598=7338543, hh:=465 +612: w0 218453 h:=7338543+218453=7556996, hh:=479 +613: set2 8737("2221) type=0 h:=7556996+630598=8187594, hh:=519 +616: set2 8574("217E) type=0 h:=8187594+630598=8818192, hh:=559 +619: set2 13377("3441) type=0 h:=8818192+630598=9448790, hh:=599 +622: set2 15226("3B7A) type=0 h:=9448790+630598=10079388, hh:=639 +[◆ ◆◇ あ◇ い◆◇ ◆◇漢字] +625: fntnum0 current font is ec-lmr10 +626: setchar83 ("53) h:=10079388+364085=10443473, hh:=662 +627: setchar97 ("61) h:=10443473+327680=10771153, hh:=683 +[Sa] +628: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +629: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +630: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +634: eop diff --git a/Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ new file mode 100644 index 00000000000..cf8d08f7715 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-ea.typ @@ -0,0 +1,825 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 1055. +maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1 +Font 1: min10 (JFM)---loaded at size 655360 DVI units +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 50623 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: ec-lmr10 +133: fntnum0 current font is ec-lmr10 +134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114 +135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126 +136: w3 218453 h:=1984283+218453=2202736, hh:=140 +[A: ] +140: set1 197("C5) h:=2202736+491520=2694256, hh:=171 +142: set1 191("BF) h:=2694256+491520=3185776, hh:=202 +144: w0 218453 h:=3185776+218453=3404229, hh:=216 +[ ] +145: set1 197("C5) h:=3404229+491520=3895749, hh:=247 +147: set1 191("BF) h:=3895749+491520=4387269, hh:=278 +149: w0 218453 h:=4387269+218453=4605722, hh:=292 +[ ] +150: fntdef1 1: min10 +171: fntnum1 current font is min10 +172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332 +[䔿] +175: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +176: y3 786432 v:=655360+786432=1441792, vv:=91 +180: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +181: push +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +185: pop +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +186: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +190: fntnum0 current font is ec-lmr10 +191: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96 +193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114 +194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135 +195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158 +196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170 +197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193 +198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214 +199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226 +[\meaning.] +200: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +201: y0 786432 v:=1441792+786432=2228224, vv:=141 +202: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +203: right3 1310720 h:=0+1310720=1310720, hh:=83 +207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287 +216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308 +217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326 +218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342 +219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363 +220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375 +221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389 +222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421 +[ macro:->macro:->] +223: set1 197("C5) h:=6630046+491520=7121566, hh:=452 +225: set1 191("BF) h:=7121566+491520=7613086, hh:=483 +227: w3 218453 h:=7613086+218453=7831539, hh:=496 +[ ] +231: set1 197("C5) h:=7831539+491520=8323059, hh:=527 +233: set1 191("BF) h:=8323059+491520=8814579, hh:=558 +235: w0 218453 h:=8814579+218453=9033032, hh:=572 +[ ] +236: fntnum1 current font is min10 +237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612 +[䔿] +240: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +241: y0 786432 v:=2228224+786432=3014656, vv:=191 +242: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +243: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +247: fntnum0 current font is ec-lmr10 +248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +[macro:->] +256: set1 197("C5) h:=3970383+491520=4461903, hh:=283 +258: set1 191("BF) h:=4461903+491520=4953423, hh:=314 +260: w3 218453 h:=4953423+218453=5171876, hh:=328 +[ ] +264: set1 197("C5) h:=5171876+491520=5663396, hh:=359 +266: set1 191("BF) h:=5663396+491520=6154916, hh:=390 +268: w0 218453 h:=6154916+218453=6373369, hh:=404 +[ ] +269: fntnum1 current font is min10 +270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444 +[䔿] +273: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +274: y0 786432 v:=3014656+786432=3801088, vv:=241 +275: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +276: push +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +280: pop +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +281: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +285: fntnum0 current font is ec-lmr10 +286: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74 +288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95 +289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118 +290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141 +291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162 +292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197 +293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215 +294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227 +[\jobname.] +295: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +296: y0 786432 v:=3801088+786432=4587520, vv:=291 +297: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +298: push +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +299: fntnum1 current font is min10 +300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +303: pop +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +304: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +308: fntnum0 current font is ec-lmr10 +309: setchar111 ("6F) h:=630598+327680=958278, hh:=61 +310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84 +311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102 +312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114 +313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132 +314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148 +315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164 +316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182 +317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198 +318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210 +[oneletter.] +319: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +320: y0 786432 v:=4587520+786432=5373952, vv:=340 +321: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +322: right3 1310720 h:=0+1310720=1310720, hh:=83 +326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +327: set1 196("C4) h:=1638400+491520=2129920, hh:=135 +329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147 +330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168 +[.\] +331: set1 197("C5) h:=2639643+491520=3131163, hh:=199 +333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211 +334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232 +[.\] +335: set1 255("FF) h:=3640886+327680=3968566, hh:=253 +337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265 +338: right3 291271 h:=4150609+291271=4441880, hh:=281 +[. ] +342: set1 191("BF) h:=4441880+491520=4933400, hh:=312 +344: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +345: y0 786432 v:=5373952+786432=6160384, vv:=390 +346: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +347: push +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +348: fntnum1 current font is min10 +349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +352: pop +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +353: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +357: fntnum0 current font is ec-lmr10 +358: setchar99 ("63) h:=630598+291275=921873, hh:=58 +359: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192 +365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname1.] +366: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +367: y0 786432 v:=6160384+786432=6946816, vv:=440 +368: push +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +369: right3 1310720 h:=0+1310720=1310720, hh:=83 +373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +374: set1 197("C5) h:=1638400+491520=2129920, hh:=135 +376: set1 191("BF) h:=2129920+491520=2621440, hh:=166 +378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187 +[\] +379: set1 197("C5) h:=2949120+491520=3440640, hh:=218 +381: set1 191("BF) h:=3440640+491520=3932160, hh:=249 +383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261 +384: w3 218453 h:=4114203+218453=4332656, hh:=274 +388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295 +[, \] +389: set1 197("C5) h:=4660336+491520=5151856, hh:=326 +391: set1 191("BF) h:=5151856+491520=5643376, hh:=357 +393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369 +394: w0 218453 h:=5825419+218453=6043872, hh:=383 +395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404 +[, \] +396: fntnum1 current font is min10 +397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444 +[䔿] +400: pop +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +401: y0 786432 v:=6946816+786432=7733248, vv:=490 +402: push +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +403: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +407: fntnum0 current font is ec-lmr10 +408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[\] +409: fntnum1 current font is min10 +410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144 +[␢] +413: fntnum0 current font is ec-lmr10 +414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176 +415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208 +416: right3 218453 h:=3288474+218453=3506927, hh:=222 +420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245 +421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266 +422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287 +423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305 +424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317 +[=> hoge,] +425: pop +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +426: y0 786432 v:=7733248+786432=8519680, vv:=540 +427: push +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +428: right3 1310720 h:=0+1310720=1310720, hh:=83 +432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +433: set1 164("A4) h:=1638400+364085=2002485, hh:=127 +435: set1 162("A2) h:=2002485+291275=2293760, hh:=145 +437: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177 +438: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209 +439: right3 218453 h:=3313236+218453=3531689, hh:=224 +443: setchar112 ("70) h:=3531689+364085=3895774, hh:=247 +444: setchar105 ("69) h:=3895774+182043=4077817, hh:=259 +445: setchar121 ("79) h:=4077817+345898=4423715, hh:=281 +446: right2 -18205 h:=4423715-18205=4405510, hh:=280 +449: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301 +450: setchar44 ("2C) h:=4733190+182043=4915233, hh:=313 +[=> piyo,] +451: pop +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +452: y0 786432 v:=8519680+786432=9306112, vv:=589 +453: push +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +454: right3 1310720 h:=0+1310720=1310720, hh:=83 +458: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +459: set1 164("A4) h:=1638400+364085=2002485, hh:=127 +461: set1 162("A2) h:=2002485+291275=2293760, hh:=145 +463: setchar61 ("3D) h:=2293760+509738=2803498, hh:=177 +464: setchar62 ("3E) h:=2803498+509738=3313236, hh:=209 +465: right3 218453 h:=3313236+218453=3531689, hh:=224 +469: setchar112 ("70) h:=3531689+364085=3895774, hh:=247 +470: setchar105 ("69) h:=3895774+182043=4077817, hh:=259 +471: setchar121 ("79) h:=4077817+345898=4423715, hh:=281 +472: right2 -18205 h:=4423715-18205=4405510, hh:=280 +475: setchar111 ("6F) h:=4405510+327680=4733190, hh:=301 +476: setchar46 ("2E) h:=4733190+182043=4915233, hh:=313 +[=> piyo.] +477: pop +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +478: y0 786432 v:=9306112+786432=10092544, vv:=639 +479: push +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +480: push +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +481: fntnum1 current font is min10 +482: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +485: pop +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +486: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +490: fntnum0 current font is ec-lmr10 +491: setchar48 ("30) h:=630598+327680=958278, hh:=61 +492: setchar120 ("78) h:=958278+345898=1304176, hh:=83 +493: setchar70 ("46) h:=1304176+427818=1731994, hh:=110 +494: setchar70 ("46) h:=1731994+427818=2159812, hh:=137 +495: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149 +[0xFF.] +496: pop +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +497: y0 786432 v:=10092544+786432=10878976, vv:=689 +498: push +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +499: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +503: fntnum1 current font is min10 +504: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +507: fntnum0 current font is ec-lmr10 +508: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +509: w3 218453 h:=2123361+218453=2341814, hh:=148 +513: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +514: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +515: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +517: set1 197("C5) h:=3252044+491520=3743564, hh:=237 +519: set1 255("FF) h:=3743564+327680=4071244, hh:=258 +521: set1 191("BF) h:=4071244+491520=4562764, hh:=289 +523: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301 +524: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336 +525: setchar97 ("61) h:=5290918+327680=5618598, hh:=357 +526: setchar99 ("63) h:=5618598+291275=5909873, hh:=375 +527: setchar114 ("72) h:=5909873+256683=6166556, hh:=391 +528: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412 +529: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424 +530: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438 +531: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470 +[:macro:->] +532: fntnum1 current font is min10 +533: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510 +[␢] +536: fntnum0 current font is ec-lmr10 +537: setchar41 ("29) h:=8035068+254870=8289938, hh:=526 +538: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538 +539: w0 218453 h:=8471981+218453=8690434, hh:=550 +540: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571 +[), \] +541: set1 255("FF) h:=9018114+327680=9345794, hh:=592 +543: set1 197("C5) h:=9345794+491520=9837314, hh:=623 +545: set1 255("FF) h:=9837314+327680=10164994, hh:=644 +547: set1 191("BF) h:=10164994+491520=10656514, hh:=675 +549: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687 +550: w0 218453 h:=10838557+218453=11057010, hh:=700 +551: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735 +552: setchar97 ("61) h:=11603121+327680=11930801, hh:=756 +553: setchar99 ("63) h:=11930801+291275=12222076, hh:=774 +554: setchar114 ("72) h:=12222076+256683=12478759, hh:=790 +555: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811 +556: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823 +557: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837 +558: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869 +559: setchar40 ("28) h:=13716673+254870=13971543, hh:=885 +560: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906 +[, macro:->(\] +561: set1 255("FF) h:=14299223+327680=14626903, hh:=927 +563: set1 197("C5) h:=14626903+491520=15118423, hh:=958 +565: set1 255("FF) h:=15118423+327680=15446103, hh:=979 +567: set1 191("BF) h:=15446103+491520=15937623, hh:=1010 +569: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022 +570: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057 +571: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078 +572: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096 +573: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112 +574: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133 +575: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145 +576: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159 +577: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191 +[:macro:->] +578: fntnum1 current font is min10 +579: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231 +[␢] +582: fntnum0 current font is ec-lmr10 +583: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247 +584: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259 +[).] +585: pop +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +586: y0 786432 v:=10878976+786432=11665408, vv:=739 +587: push +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +588: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +592: fntnum1 current font is min10 +593: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +596: fntnum0 current font is ec-lmr10 +597: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +598: w3 218453 h:=2123361+218453=2341814, hh:=148 +602: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +603: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +604: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +606: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218 +607: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253 +608: setchar97 ("61) h:=3980198+327680=4307878, hh:=274 +609: setchar99 ("63) h:=4307878+291275=4599153, hh:=292 +610: setchar114 ("72) h:=4599153+256683=4855836, hh:=308 +611: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329 +612: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341 +613: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355 +614: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387 +[:macro:->] +615: fntnum1 current font is min10 +616: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427 +[] +619: fntnum0 current font is ec-lmr10 +620: setchar41 ("29) h:=6724348+254870=6979218, hh:=443 +621: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455 +622: w0 218453 h:=7161261+218453=7379714, hh:=467 +623: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488 +[), \] +624: set1 255("FF) h:=7707394+327680=8035074, hh:=509 +626: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521 +627: w0 218453 h:=8217117+218453=8435570, hh:=534 +628: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569 +629: setchar97 ("61) h:=8981681+327680=9309361, hh:=590 +630: setchar99 ("63) h:=9309361+291275=9600636, hh:=608 +631: setchar114 ("72) h:=9600636+256683=9857319, hh:=624 +632: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645 +633: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657 +634: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671 +635: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703 +636: setchar40 ("28) h:=11095233+254870=11350103, hh:=719 +637: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740 +[, macro:->(\] +638: set1 255("FF) h:=11677783+327680=12005463, hh:=761 +640: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773 +641: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808 +642: setchar97 ("61) h:=12733617+327680=13061297, hh:=829 +643: setchar99 ("63) h:=13061297+291275=13352572, hh:=847 +644: setchar114 ("72) h:=13352572+256683=13609255, hh:=863 +645: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884 +646: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896 +647: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910 +648: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942 +[:macro:->] +649: fntnum1 current font is min10 +650: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982 +[] +653: fntnum0 current font is ec-lmr10 +654: setchar41 ("29) h:=15477767+254870=15732637, hh:=998 +655: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010 +[).] +656: pop +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +657: y0 786432 v:=11665408+786432=12451840, vv:=789 +658: push +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +659: push +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +660: fntnum1 current font is min10 +661: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +664: pop +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +665: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +669: fntnum0 current font is ec-lmr10 +670: setchar99 ("63) h:=630598+291275=921873, hh:=58 +671: setchar111 ("6F) h:=921873+327680=1249553, hh:=79 +672: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102 +673: right2 -18205 h:=1613638-18205=1595433, hh:=101 +676: setchar116 ("74) h:=1595433+254870=1850303, hh:=117 +677: setchar101 ("65) h:=1850303+291275=2141578, hh:=135 +678: setchar120 ("78) h:=2141578+345898=2487476, hh:=157 +679: setchar116 ("74) h:=2487476+254870=2742346, hh:=173 +680: setchar115 ("73) h:=2742346+258506=3000852, hh:=189 +681: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201 +[contexts.] +682: pop +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +683: y0 786432 v:=12451840+786432=13238272, vv:=839 +684: push +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +685: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +689: fntnum1 current font is min10 +690: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +693: fntnum0 current font is ec-lmr10 +694: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +696: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +698: w3 218453 h:=2924358+218453=3142811, hh:=199 +702: setchar97 ("61) h:=3142811+327680=3470491, hh:=220 +[ a] +703: set1 197("C5) h:=3470491+491520=3962011, hh:=251 +705: set1 191("BF) h:=3962011+491520=4453531, hh:=282 +707: w0 218453 h:=4453531+218453=4671984, hh:=296 +[ ] +708: set1 197("C5) h:=4671984+491520=5163504, hh:=327 +710: set1 191("BF) h:=5163504+491520=5655024, hh:=358 +712: w0 218453 h:=5655024+218453=5873477, hh:=372 +[ ] +713: fntnum1 current font is min10 +714: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412 +717: w0 218453 h:=6504075+218453=6722528, hh:=426 +[䔿 ] +718: fntnum0 current font is ec-lmr10 +719: set1 197("C5) h:=6722528+491520=7214048, hh:=457 +721: set1 191("BF) h:=7214048+491520=7705568, hh:=488 +723: w0 218453 h:=7705568+218453=7924021, hh:=502 +[ ] +724: set1 197("C5) h:=7924021+491520=8415541, hh:=533 +726: set1 191("BF) h:=8415541+491520=8907061, hh:=564 +728: w0 218453 h:=8907061+218453=9125514, hh:=578 +[ ] +729: fntnum1 current font is min10 +730: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618 +733: x3 436906 h:=9756112+436906=10193018, hh:=646 +[䔿 ] +737: fntnum0 current font is ec-lmr10 +738: set1 197("C5) h:=10193018+491520=10684538, hh:=677 +740: set1 191("BF) h:=10684538+491520=11176058, hh:=708 +742: w0 218453 h:=11176058+218453=11394511, hh:=722 +[ ] +743: fntnum1 current font is min10 +744: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762 +747: x0 436906 h:=12025109+436906=12462015, hh:=789 +748: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829 +[䔿 ] +751: fntnum0 current font is ec-lmr10 +752: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864 +753: setchar97 ("61) h:=13638724+327680=13966404, hh:=885 +754: setchar99 ("63) h:=13966404+291275=14257679, hh:=903 +755: setchar114 ("72) h:=14257679+256683=14514362, hh:=919 +756: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940 +757: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952 +758: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966 +759: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998 +[macro:->] +760: set1 197("C5) h:=15752276+491520=16243796, hh:=1029 +762: set1 191("BF) h:=16243796+491520=16735316, hh:=1060 +764: w0 218453 h:=16735316+218453=16953769, hh:=1074 +[ ] +765: set1 197("C5) h:=16953769+491520=17445289, hh:=1105 +767: set1 191("BF) h:=17445289+491520=17936809, hh:=1136 +769: w0 218453 h:=17936809+218453=18155262, hh:=1150 +[ ] +770: fntnum1 current font is min10 +771: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190 +[䔿] +774: pop +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +775: y0 786432 v:=13238272+786432=14024704, vv:=888 +776: push +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +777: right3 1310720 h:=0+1310720=1310720, hh:=83 +781: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[ ␢] +784: fntnum0 current font is ec-lmr10 +785: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +787: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +789: w3 218453 h:=2924358+218453=3142811, hh:=199 +[ ] +793: set1 197("C5) h:=3142811+491520=3634331, hh:=230 +795: set1 191("BF) h:=3634331+491520=4125851, hh:=261 +797: fntnum1 current font is min10 +798: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301 +[䔿] +801: fntnum0 current font is ec-lmr10 +802: set1 197("C5) h:=4756449+491520=5247969, hh:=332 +804: set1 191("BF) h:=5247969+491520=5739489, hh:=363 +806: w0 218453 h:=5739489+218453=5957942, hh:=377 +807: setchar97 ("61) h:=5957942+327680=6285622, hh:=398 +[ a] +808: set1 197("C5) h:=6285622+491520=6777142, hh:=429 +810: set1 191("BF) h:=6777142+491520=7268662, hh:=460 +812: w0 218453 h:=7268662+218453=7487115, hh:=474 +[ ] +813: set1 197("C5) h:=7487115+491520=7978635, hh:=505 +815: set1 191("BF) h:=7978635+491520=8470155, hh:=536 +817: w0 218453 h:=8470155+218453=8688608, hh:=550 +[ ] +818: fntnum1 current font is min10 +819: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590 +822: w0 218453 h:=9319206+218453=9537659, hh:=604 +[䔿 ] +823: fntnum0 current font is ec-lmr10 +824: set1 197("C5) h:=9537659+491520=10029179, hh:=635 +826: set1 191("BF) h:=10029179+491520=10520699, hh:=666 +828: w0 218453 h:=10520699+218453=10739152, hh:=680 +[ ] +829: set1 197("C5) h:=10739152+491520=11230672, hh:=711 +831: set1 191("BF) h:=11230672+491520=11722192, hh:=742 +833: w0 218453 h:=11722192+218453=11940645, hh:=756 +[ ] +834: fntnum1 current font is min10 +835: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796 +838: right3 436906 h:=12571243+436906=13008149, hh:=824 +[䔿 ] +842: fntnum0 current font is ec-lmr10 +843: set1 197("C5) h:=13008149+491520=13499669, hh:=855 +845: set1 191("BF) h:=13499669+491520=13991189, hh:=886 +847: w0 218453 h:=13991189+218453=14209642, hh:=900 +[ ] +848: fntnum1 current font is min10 +849: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940 +852: w0 218453 h:=14840240+218453=15058693, hh:=954 +853: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994 +[䔿 ] +856: fntnum0 current font is ec-lmr10 +857: set1 255("FF) h:=15689291+327680=16016971, hh:=1015 +859: pop +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +860: y0 786432 v:=14024704+786432=14811136, vv:=938 +861: push +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +862: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +866: fntnum1 current font is min10 +867: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +870: pop +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +871: y0 786432 v:=14811136+786432=15597568, vv:=988 +872: push +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +873: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +877: fntnum0 current font is ec-lmr10 +878: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +879: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +880: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +881: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +882: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +883: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +884: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +885: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +886: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +887: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[.a...c.b..] +888: pop +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +889: y0 786432 v:=15597568+786432=16384000, vv:=1038 +890: push +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +891: right3 1310720 h:=0+1310720=1310720, hh:=83 +895: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +896: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +897: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +898: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +899: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +900: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +901: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +902: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +903: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +904: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[ .a...c.b..] +905: pop +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +906: y0 786432 v:=16384000+786432=17170432, vv:=1088 +907: push +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +908: push +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +909: fntnum1 current font is min10 +910: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +913: pop +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +914: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +918: fntnum0 current font is ec-lmr10 +919: setchar99 ("63) h:=630598+291275=921873, hh:=58 +920: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +921: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +922: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +923: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +924: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +925: setchar50 ("32) h:=2709530+327680=3037210, hh:=192 +926: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname2.] +927: pop +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +928: y0 786432 v:=17170432+786432=17956864, vv:=1137 +929: push +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +930: right3 1310720 h:=0+1310720=1310720, hh:=83 +934: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +935: setchar72 ("48) h:=1674805+491520=2166325, hh:=137 +936: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169 +937: right2 -18205 h:=2676063-18205=2657858, hh:=168 +940: setchar87 ("57) h:=2657858+673578=3331436, hh:=211 +941: right3 218453 h:=3331436+218453=3549889, hh:=225 +945: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237 +946: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249 +947: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261 +[ SHOW ...] +948: pop +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +949: y0 786432 v:=17956864+786432=18743296, vv:=1187 +950: push +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +951: right3 1310720 h:=0+1310720=1310720, hh:=83 +955: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +956: setchar84 ("54) h:=1674805+473301=2148106, hh:=136 +957: setchar82 ("52) h:=2148106+482410=2630516, hh:=167 +958: setchar73 ("49) h:=2630516+236650=2867166, hh:=182 +959: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213 +960: setchar71 ("47) h:=3358686+514261=3872947, hh:=246 +961: right3 218453 h:=3872947+218453=4091400, hh:=259 +965: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280 +[ STRING \] +966: set1 197("C5) h:=4419080+491520=4910600, hh:=311 +968: set1 191("BF) h:=4910600+491520=5402120, hh:=342 +970: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354 +971: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375 +[.\] +972: fntnum1 current font is min10 +973: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415 +[䔿] +976: fntnum0 current font is ec-lmr10 +977: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427 +978: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448 +[.\] +979: set1 197("C5) h:=7052164+491520=7543684, hh:=479 +981: set1 191("BF) h:=7543684+491520=8035204, hh:=510 +983: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522 +984: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543 +[.\] +985: fntnum1 current font is min10 +986: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583 +[䔿] +989: pop +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +990: y0 786432 v:=18743296+786432=19529728, vv:=1237 +991: push +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +992: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +996: fntnum0 current font is ec-lmr10 +997: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121 +998: setchar69 ("45) h:=1911488+446005=2357493, hh:=149 +999: setchar65 ("41) h:=2357493+491520=2849013, hh:=180 +1000: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211 +1001: setchar73 ("49) h:=3340533+236650=3577183, hh:=226 +1002: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257 +1003: setchar71 ("47) h:=4068703+514261=4582964, hh:=290 +1004: right3 218453 h:=4582964+218453=4801417, hh:=304 +1008: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339 +1009: setchar97 ("61) h:=5347528+327680=5675208, hh:=360 +1010: setchar99 ("63) h:=5675208+291275=5966483, hh:=378 +1011: setchar114 ("72) h:=5966483+256683=6223166, hh:=394 +1012: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415 +1013: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427 +1014: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441 +1015: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473 +1016: setchar97 ("61) h:=7461080+327680=7788760, hh:=494 +1017: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506 +1018: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541 +1019: setchar97 ("61) h:=8516914+327680=8844594, hh:=562 +1020: setchar99 ("63) h:=8844594+291275=9135869, hh:=580 +1021: setchar114 ("72) h:=9135869+256683=9392552, hh:=596 +1022: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617 +1023: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629 +1024: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643 +1025: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675 +1026: setchar98 ("62) h:=10630466+364085=10994551, hh:=698 +1027: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710 +1028: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745 +1029: setchar97 ("61) h:=11722705+327680=12050385, hh:=765 +1030: setchar99 ("63) h:=12050385+291275=12341660, hh:=783 +1031: setchar114 ("72) h:=12341660+256683=12598343, hh:=799 +1032: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820 +1033: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832 +1034: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846 +1035: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878 +1036: setchar97 ("61) h:=13836257+327680=14163937, hh:=899 +1037: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911 +1038: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945 +1039: setchar97 ("61) h:=14892091+327680=15219771, hh:=966 +1040: setchar99 ("63) h:=15219771+291275=15511046, hh:=984 +1041: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000 +1042: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021 +1043: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033 +1044: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047 +1045: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079 +1046: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102 +1047: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114 +[MEANING macro:->a.macro:->b.macro:->a.macro:->b.] +1048: pop +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +1049: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1050: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +1054: eop diff --git a/Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ new file mode 100644 index 00000000000..9373f0bac64 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/p-pknj-sa.typ @@ -0,0 +1,829 @@ +Options selected: + Starting page = * + Maximum number of pages = 1000000 + Output level = 4 (the works) + Resolution = 300.00000000 pixels per inch +numerator/denominator=25400000/473628672 +magnification=1000; 0.00006334 pixels per DVI unit +' TeX output 20XX.XX.XX' +Postamble starts at byte 1061. +maxv=43725786, maxh=30785863, maxstackdepth=3, totalpages=1 +Font 1: min10 (JFM)---loaded at size 655360 DVI units +Font 0: ec-lmr10---loaded at size 655360 DVI units + +42: beginning of page 37726 +87: push +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +88: down3 -917504 v:=0-917504=-917504, vv:=-58 +92: pop +level 0:(h=0,v=0,w=0,x=0,y=0,z=0,hh=0,vv=0) +93: down4 42152922 v:=0+42152922=42152922, vv:=2670 +98: push +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +99: down4 -41497562 v:=42152922-41497562=655360, vv:=42 +104: push +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +105: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +109: fntdef1 0: ec-lmr10 +133: fntnum0 current font is ec-lmr10 +134: setchar65 ("41) h:=1310720+491520=1802240, hh:=114 +135: setchar58 ("3A) h:=1802240+182043=1984283, hh:=126 +136: w3 218453 h:=1984283+218453=2202736, hh:=140 +[A: ] +140: set1 197("C5) h:=2202736+491520=2694256, hh:=171 +142: set1 191("BF) h:=2694256+491520=3185776, hh:=202 +144: w0 218453 h:=3185776+218453=3404229, hh:=216 +[ ] +145: set1 197("C5) h:=3404229+491520=3895749, hh:=247 +147: set1 191("BF) h:=3895749+491520=4387269, hh:=278 +149: w0 218453 h:=4387269+218453=4605722, hh:=292 +[ ] +150: fntdef1 1: min10 +171: fntnum1 current font is min10 +172: set2 17727("453F) type=0 h:=4605722+630598=5236320, hh:=332 +[䔿] +175: pop +level 1:(h=0,v=655360,w=0,x=0,y=0,z=0,hh=0,vv=42) +176: y3 786432 v:=655360+786432=1441792, vv:=91 +180: push +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +181: push +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +182: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +185: pop +level 2:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +186: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +190: fntnum0 current font is ec-lmr10 +191: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +192: setchar109 ("6D) h:=958278+546111=1504389, hh:=96 +193: setchar101 ("65) h:=1504389+291275=1795664, hh:=114 +194: setchar97 ("61) h:=1795664+327680=2123344, hh:=135 +195: setchar110 ("6E) h:=2123344+364085=2487429, hh:=158 +196: setchar105 ("69) h:=2487429+182043=2669472, hh:=170 +197: setchar110 ("6E) h:=2669472+364085=3033557, hh:=193 +198: setchar103 ("67) h:=3033557+327680=3361237, hh:=214 +199: setchar46 ("2E) h:=3361237+182043=3543280, hh:=226 +[\meaning.] +200: pop +level 1:(h=0,v=1441792,w=0,x=0,y=786432,z=0,hh=0,vv=91) +201: y0 786432 v:=1441792+786432=2228224, vv:=141 +202: push +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +203: right3 1310720 h:=0+1310720=1310720, hh:=83 +207: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +208: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +209: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +210: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +211: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +212: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +213: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +214: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +215: setchar109 ("6D) h:=3970383+546111=4516494, hh:=287 +216: setchar97 ("61) h:=4516494+327680=4844174, hh:=308 +217: setchar99 ("63) h:=4844174+291275=5135449, hh:=326 +218: setchar114 ("72) h:=5135449+256683=5392132, hh:=342 +219: setchar111 ("6F) h:=5392132+327680=5719812, hh:=363 +220: setchar58 ("3A) h:=5719812+182043=5901855, hh:=375 +221: setchar45 ("2D) h:=5901855+218453=6120308, hh:=389 +222: setchar62 ("3E) h:=6120308+509738=6630046, hh:=421 +[ macro:->macro:->] +223: set1 197("C5) h:=6630046+491520=7121566, hh:=452 +225: set1 191("BF) h:=7121566+491520=7613086, hh:=483 +227: w3 218453 h:=7613086+218453=7831539, hh:=496 +[ ] +231: set1 197("C5) h:=7831539+491520=8323059, hh:=527 +233: set1 191("BF) h:=8323059+491520=8814579, hh:=558 +235: w0 218453 h:=8814579+218453=9033032, hh:=572 +[ ] +236: fntnum1 current font is min10 +237: set2 17727("453F) type=0 h:=9033032+630598=9663630, hh:=612 +[䔿] +240: pop +level 1:(h=0,v=2228224,w=0,x=0,y=786432,z=0,hh=0,vv=141) +241: y0 786432 v:=2228224+786432=3014656, vv:=191 +242: push +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +243: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +247: fntnum0 current font is ec-lmr10 +248: setchar109 ("6D) h:=1310720+546111=1856831, hh:=118 +249: setchar97 ("61) h:=1856831+327680=2184511, hh:=139 +250: setchar99 ("63) h:=2184511+291275=2475786, hh:=157 +251: setchar114 ("72) h:=2475786+256683=2732469, hh:=173 +252: setchar111 ("6F) h:=2732469+327680=3060149, hh:=194 +253: setchar58 ("3A) h:=3060149+182043=3242192, hh:=206 +254: setchar45 ("2D) h:=3242192+218453=3460645, hh:=220 +255: setchar62 ("3E) h:=3460645+509738=3970383, hh:=252 +[macro:->] +256: set1 197("C5) h:=3970383+491520=4461903, hh:=283 +258: set1 191("BF) h:=4461903+491520=4953423, hh:=314 +260: w3 218453 h:=4953423+218453=5171876, hh:=328 +[ ] +264: set1 197("C5) h:=5171876+491520=5663396, hh:=359 +266: set1 191("BF) h:=5663396+491520=6154916, hh:=390 +268: w0 218453 h:=6154916+218453=6373369, hh:=404 +[ ] +269: fntnum1 current font is min10 +270: set2 17727("453F) type=0 h:=6373369+630598=7003967, hh:=444 +[䔿] +273: pop +level 1:(h=0,v=3014656,w=0,x=0,y=786432,z=0,hh=0,vv=191) +274: y0 786432 v:=3014656+786432=3801088, vv:=241 +275: push +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +276: push +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +277: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +280: pop +level 2:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +281: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +285: fntnum0 current font is ec-lmr10 +286: setchar92 ("5C) h:=630598+327680=958278, hh:=61 +287: setchar106 ("6A) h:=958278+200245=1158523, hh:=74 +288: setchar111 ("6F) h:=1158523+327680=1486203, hh:=95 +289: setchar98 ("62) h:=1486203+364085=1850288, hh:=118 +290: setchar110 ("6E) h:=1850288+364085=2214373, hh:=141 +291: setchar97 ("61) h:=2214373+327680=2542053, hh:=162 +292: setchar109 ("6D) h:=2542053+546111=3088164, hh:=197 +293: setchar101 ("65) h:=3088164+291275=3379439, hh:=215 +294: setchar46 ("2E) h:=3379439+182043=3561482, hh:=227 +[\jobname.] +295: pop +level 1:(h=0,v=3801088,w=0,x=0,y=786432,z=0,hh=0,vv=241) +296: y0 786432 v:=3801088+786432=4587520, vv:=291 +297: push +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +298: push +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +299: fntnum1 current font is min10 +300: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +303: pop +level 2:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +304: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +308: fntnum0 current font is ec-lmr10 +309: setchar111 ("6F) h:=630598+327680=958278, hh:=61 +310: setchar110 ("6E) h:=958278+364085=1322363, hh:=84 +311: setchar101 ("65) h:=1322363+291275=1613638, hh:=102 +312: setchar108 ("6C) h:=1613638+182043=1795681, hh:=114 +313: setchar101 ("65) h:=1795681+291275=2086956, hh:=132 +314: setchar116 ("74) h:=2086956+254870=2341826, hh:=148 +315: setchar116 ("74) h:=2341826+254870=2596696, hh:=164 +316: setchar101 ("65) h:=2596696+291275=2887971, hh:=182 +317: setchar114 ("72) h:=2887971+256683=3144654, hh:=198 +318: setchar46 ("2E) h:=3144654+182043=3326697, hh:=210 +[oneletter.] +319: pop +level 1:(h=0,v=4587520,w=0,x=0,y=786432,z=0,hh=0,vv=291) +320: y0 786432 v:=4587520+786432=5373952, vv:=340 +321: push +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +322: right3 1310720 h:=0+1310720=1310720, hh:=83 +326: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +327: set1 196("C4) h:=1638400+491520=2129920, hh:=135 +329: setchar46 ("2E) h:=2129920+182043=2311963, hh:=147 +330: setchar92 ("5C) h:=2311963+327680=2639643, hh:=168 +[.\] +331: set1 197("C5) h:=2639643+491520=3131163, hh:=199 +333: setchar46 ("2E) h:=3131163+182043=3313206, hh:=211 +334: setchar92 ("5C) h:=3313206+327680=3640886, hh:=232 +[.\] +335: set1 255("FF) h:=3640886+327680=3968566, hh:=253 +337: setchar46 ("2E) h:=3968566+182043=4150609, hh:=265 +338: right3 291271 h:=4150609+291271=4441880, hh:=281 +[. ] +342: set1 191("BF) h:=4441880+491520=4933400, hh:=312 +344: pop +level 1:(h=0,v=5373952,w=0,x=0,y=786432,z=0,hh=0,vv=340) +345: y0 786432 v:=5373952+786432=6160384, vv:=390 +346: push +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +347: push +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +348: fntnum1 current font is min10 +349: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +352: pop +level 2:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +353: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +357: fntnum0 current font is ec-lmr10 +358: setchar99 ("63) h:=630598+291275=921873, hh:=58 +359: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +360: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +361: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +362: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +363: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +364: setchar49 ("31) h:=2709530+327680=3037210, hh:=192 +365: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname1.] +366: pop +level 1:(h=0,v=6160384,w=0,x=0,y=786432,z=0,hh=0,vv=390) +367: y0 786432 v:=6160384+786432=6946816, vv:=440 +368: push +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +369: right3 1310720 h:=0+1310720=1310720, hh:=83 +373: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +374: set1 197("C5) h:=1638400+491520=2129920, hh:=135 +376: set1 191("BF) h:=2129920+491520=2621440, hh:=166 +378: setchar92 ("5C) h:=2621440+327680=2949120, hh:=187 +[\] +379: set1 197("C5) h:=2949120+491520=3440640, hh:=218 +381: set1 191("BF) h:=3440640+491520=3932160, hh:=249 +383: setchar44 ("2C) h:=3932160+182043=4114203, hh:=261 +384: w3 218453 h:=4114203+218453=4332656, hh:=274 +388: setchar92 ("5C) h:=4332656+327680=4660336, hh:=295 +[, \] +389: set1 197("C5) h:=4660336+491520=5151856, hh:=326 +391: set1 191("BF) h:=5151856+491520=5643376, hh:=357 +393: setchar44 ("2C) h:=5643376+182043=5825419, hh:=369 +394: w0 218453 h:=5825419+218453=6043872, hh:=383 +395: setchar92 ("5C) h:=6043872+327680=6371552, hh:=404 +[, \] +396: fntnum1 current font is min10 +397: set2 17727("453F) type=0 h:=6371552+630598=7002150, hh:=444 +[䔿] +400: pop +level 1:(h=0,v=6946816,w=0,x=0,y=786432,z=0,hh=0,vv=440) +401: y0 786432 v:=6946816+786432=7733248, vv:=490 +402: push +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +403: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +407: fntnum0 current font is ec-lmr10 +408: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[\] +409: fntnum1 current font is min10 +410: set2 9250("2422) type=0 h:=1638400+630598=2268998, hh:=144 +[␢] +413: fntnum0 current font is ec-lmr10 +414: setchar61 ("3D) h:=2268998+509738=2778736, hh:=176 +415: setchar62 ("3E) h:=2778736+509738=3288474, hh:=208 +416: right3 218453 h:=3288474+218453=3506927, hh:=222 +420: setchar104 ("68) h:=3506927+364085=3871012, hh:=245 +421: setchar111 ("6F) h:=3871012+327680=4198692, hh:=266 +422: setchar103 ("67) h:=4198692+327680=4526372, hh:=287 +423: setchar101 ("65) h:=4526372+291275=4817647, hh:=305 +424: setchar44 ("2C) h:=4817647+182043=4999690, hh:=317 +[=> hoge,] +425: pop +level 1:(h=0,v=7733248,w=0,x=0,y=786432,z=0,hh=0,vv=490) +426: y0 786432 v:=7733248+786432=8519680, vv:=540 +427: push +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +428: right3 1310720 h:=0+1310720=1310720, hh:=83 +432: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +433: set1 227("E3) h:=1638400+327680=1966080, hh:=125 +435: set1 129("81) h:=1966080+491520=2457600, hh:=156 +437: w2 -18205 h:=2457600-18205=2439395, hh:=155 +440: set1 130("82) h:=2439395+473301=2912696, hh:=185 +442: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217 +443: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249 +444: right3 218453 h:=3932172+218453=4150625, hh:=263 +448: setchar112 ("70) h:=4150625+364085=4514710, hh:=286 +449: setchar105 ("69) h:=4514710+182043=4696753, hh:=298 +450: setchar121 ("79) h:=4696753+345898=5042651, hh:=320 +451: w0 -18205 h:=5042651-18205=5024446, hh:=319 +452: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340 +453: setchar44 ("2C) h:=5352126+182043=5534169, hh:=352 +[=> piyo,] +454: pop +level 1:(h=0,v=8519680,w=0,x=0,y=786432,z=0,hh=0,vv=540) +455: y0 786432 v:=8519680+786432=9306112, vv:=589 +456: push +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +457: right3 1310720 h:=0+1310720=1310720, hh:=83 +461: setchar92 ("5C) h:=1310720+327680=1638400, hh:=104 +[ \] +462: set1 227("E3) h:=1638400+327680=1966080, hh:=125 +464: set1 129("81) h:=1966080+491520=2457600, hh:=156 +466: w2 -18205 h:=2457600-18205=2439395, hh:=155 +469: set1 130("82) h:=2439395+473301=2912696, hh:=185 +471: setchar61 ("3D) h:=2912696+509738=3422434, hh:=217 +472: setchar62 ("3E) h:=3422434+509738=3932172, hh:=249 +473: right3 218453 h:=3932172+218453=4150625, hh:=263 +477: setchar112 ("70) h:=4150625+364085=4514710, hh:=286 +478: setchar105 ("69) h:=4514710+182043=4696753, hh:=298 +479: setchar121 ("79) h:=4696753+345898=5042651, hh:=320 +480: w0 -18205 h:=5042651-18205=5024446, hh:=319 +481: setchar111 ("6F) h:=5024446+327680=5352126, hh:=340 +482: setchar46 ("2E) h:=5352126+182043=5534169, hh:=352 +[=> piyo.] +483: pop +level 1:(h=0,v=9306112,w=0,x=0,y=786432,z=0,hh=0,vv=589) +484: y0 786432 v:=9306112+786432=10092544, vv:=639 +485: push +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +486: push +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +487: fntnum1 current font is min10 +488: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +491: pop +level 2:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +492: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +496: fntnum0 current font is ec-lmr10 +497: setchar48 ("30) h:=630598+327680=958278, hh:=61 +498: setchar120 ("78) h:=958278+345898=1304176, hh:=83 +499: setchar70 ("46) h:=1304176+427818=1731994, hh:=110 +500: setchar70 ("46) h:=1731994+427818=2159812, hh:=137 +501: setchar46 ("2E) h:=2159812+182043=2341855, hh:=149 +[0xFF.] +502: pop +level 1:(h=0,v=10092544,w=0,x=0,y=786432,z=0,hh=0,vv=639) +503: y0 786432 v:=10092544+786432=10878976, vv:=689 +504: push +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +505: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +509: fntnum1 current font is min10 +510: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +513: fntnum0 current font is ec-lmr10 +514: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +515: w3 218453 h:=2123361+218453=2341814, hh:=148 +519: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +520: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +521: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +523: set1 197("C5) h:=3252044+491520=3743564, hh:=237 +525: set1 255("FF) h:=3743564+327680=4071244, hh:=258 +527: set1 191("BF) h:=4071244+491520=4562764, hh:=289 +529: setchar58 ("3A) h:=4562764+182043=4744807, hh:=301 +530: setchar109 ("6D) h:=4744807+546111=5290918, hh:=336 +531: setchar97 ("61) h:=5290918+327680=5618598, hh:=357 +532: setchar99 ("63) h:=5618598+291275=5909873, hh:=375 +533: setchar114 ("72) h:=5909873+256683=6166556, hh:=391 +534: setchar111 ("6F) h:=6166556+327680=6494236, hh:=412 +535: setchar58 ("3A) h:=6494236+182043=6676279, hh:=424 +536: setchar45 ("2D) h:=6676279+218453=6894732, hh:=438 +537: setchar62 ("3E) h:=6894732+509738=7404470, hh:=470 +[:macro:->] +538: fntnum1 current font is min10 +539: set2 9250("2422) type=0 h:=7404470+630598=8035068, hh:=510 +[␢] +542: fntnum0 current font is ec-lmr10 +543: setchar41 ("29) h:=8035068+254870=8289938, hh:=526 +544: setchar44 ("2C) h:=8289938+182043=8471981, hh:=538 +545: w0 218453 h:=8471981+218453=8690434, hh:=550 +546: setchar92 ("5C) h:=8690434+327680=9018114, hh:=571 +[), \] +547: set1 255("FF) h:=9018114+327680=9345794, hh:=592 +549: set1 197("C5) h:=9345794+491520=9837314, hh:=623 +551: set1 255("FF) h:=9837314+327680=10164994, hh:=644 +553: set1 191("BF) h:=10164994+491520=10656514, hh:=675 +555: setchar44 ("2C) h:=10656514+182043=10838557, hh:=687 +556: w0 218453 h:=10838557+218453=11057010, hh:=700 +557: setchar109 ("6D) h:=11057010+546111=11603121, hh:=735 +558: setchar97 ("61) h:=11603121+327680=11930801, hh:=756 +559: setchar99 ("63) h:=11930801+291275=12222076, hh:=774 +560: setchar114 ("72) h:=12222076+256683=12478759, hh:=790 +561: setchar111 ("6F) h:=12478759+327680=12806439, hh:=811 +562: setchar58 ("3A) h:=12806439+182043=12988482, hh:=823 +563: setchar45 ("2D) h:=12988482+218453=13206935, hh:=837 +564: setchar62 ("3E) h:=13206935+509738=13716673, hh:=869 +565: setchar40 ("28) h:=13716673+254870=13971543, hh:=885 +566: setchar92 ("5C) h:=13971543+327680=14299223, hh:=906 +[, macro:->(\] +567: set1 255("FF) h:=14299223+327680=14626903, hh:=927 +569: set1 197("C5) h:=14626903+491520=15118423, hh:=958 +571: set1 255("FF) h:=15118423+327680=15446103, hh:=979 +573: set1 191("BF) h:=15446103+491520=15937623, hh:=1010 +575: setchar58 ("3A) h:=15937623+182043=16119666, hh:=1022 +576: setchar109 ("6D) h:=16119666+546111=16665777, hh:=1057 +577: setchar97 ("61) h:=16665777+327680=16993457, hh:=1078 +578: setchar99 ("63) h:=16993457+291275=17284732, hh:=1096 +579: setchar114 ("72) h:=17284732+256683=17541415, hh:=1112 +580: setchar111 ("6F) h:=17541415+327680=17869095, hh:=1133 +581: setchar58 ("3A) h:=17869095+182043=18051138, hh:=1145 +582: setchar45 ("2D) h:=18051138+218453=18269591, hh:=1159 +583: setchar62 ("3E) h:=18269591+509738=18779329, hh:=1191 +[:macro:->] +584: fntnum1 current font is min10 +585: set2 9250("2422) type=0 h:=18779329+630598=19409927, hh:=1231 +[␢] +588: fntnum0 current font is ec-lmr10 +589: setchar41 ("29) h:=19409927+254870=19664797, hh:=1247 +590: setchar46 ("2E) h:=19664797+182043=19846840, hh:=1259 +[).] +591: pop +level 1:(h=0,v=10878976,w=0,x=0,y=786432,z=0,hh=0,vv=689) +592: y0 786432 v:=10878976+786432=11665408, vv:=739 +593: push +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +594: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +598: fntnum1 current font is min10 +599: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +602: fntnum0 current font is ec-lmr10 +603: setchar44 ("2C) h:=1941318+182043=2123361, hh:=135 +604: w3 218453 h:=2123361+218453=2341814, hh:=148 +608: setchar40 ("28) h:=2341814+254870=2596684, hh:=164 +609: setchar92 ("5C) h:=2596684+327680=2924364, hh:=185 +[, (\] +610: set1 255("FF) h:=2924364+327680=3252044, hh:=206 +612: setchar58 ("3A) h:=3252044+182043=3434087, hh:=218 +613: setchar109 ("6D) h:=3434087+546111=3980198, hh:=253 +614: setchar97 ("61) h:=3980198+327680=4307878, hh:=274 +615: setchar99 ("63) h:=4307878+291275=4599153, hh:=292 +616: setchar114 ("72) h:=4599153+256683=4855836, hh:=308 +617: setchar111 ("6F) h:=4855836+327680=5183516, hh:=329 +618: setchar58 ("3A) h:=5183516+182043=5365559, hh:=341 +619: setchar45 ("2D) h:=5365559+218453=5584012, hh:=355 +620: setchar62 ("3E) h:=5584012+509738=6093750, hh:=387 +[:macro:->] +621: fntnum1 current font is min10 +622: set2 9252("2424) type=0 h:=6093750+630598=6724348, hh:=427 +[] +625: fntnum0 current font is ec-lmr10 +626: setchar41 ("29) h:=6724348+254870=6979218, hh:=443 +627: setchar44 ("2C) h:=6979218+182043=7161261, hh:=455 +628: w0 218453 h:=7161261+218453=7379714, hh:=467 +629: setchar92 ("5C) h:=7379714+327680=7707394, hh:=488 +[), \] +630: set1 255("FF) h:=7707394+327680=8035074, hh:=509 +632: setchar44 ("2C) h:=8035074+182043=8217117, hh:=521 +633: w0 218453 h:=8217117+218453=8435570, hh:=534 +634: setchar109 ("6D) h:=8435570+546111=8981681, hh:=569 +635: setchar97 ("61) h:=8981681+327680=9309361, hh:=590 +636: setchar99 ("63) h:=9309361+291275=9600636, hh:=608 +637: setchar114 ("72) h:=9600636+256683=9857319, hh:=624 +638: setchar111 ("6F) h:=9857319+327680=10184999, hh:=645 +639: setchar58 ("3A) h:=10184999+182043=10367042, hh:=657 +640: setchar45 ("2D) h:=10367042+218453=10585495, hh:=671 +641: setchar62 ("3E) h:=10585495+509738=11095233, hh:=703 +642: setchar40 ("28) h:=11095233+254870=11350103, hh:=719 +643: setchar92 ("5C) h:=11350103+327680=11677783, hh:=740 +[, macro:->(\] +644: set1 255("FF) h:=11677783+327680=12005463, hh:=761 +646: setchar58 ("3A) h:=12005463+182043=12187506, hh:=773 +647: setchar109 ("6D) h:=12187506+546111=12733617, hh:=808 +648: setchar97 ("61) h:=12733617+327680=13061297, hh:=829 +649: setchar99 ("63) h:=13061297+291275=13352572, hh:=847 +650: setchar114 ("72) h:=13352572+256683=13609255, hh:=863 +651: setchar111 ("6F) h:=13609255+327680=13936935, hh:=884 +652: setchar58 ("3A) h:=13936935+182043=14118978, hh:=896 +653: setchar45 ("2D) h:=14118978+218453=14337431, hh:=910 +654: setchar62 ("3E) h:=14337431+509738=14847169, hh:=942 +[:macro:->] +655: fntnum1 current font is min10 +656: set2 9252("2424) type=0 h:=14847169+630598=15477767, hh:=982 +[] +659: fntnum0 current font is ec-lmr10 +660: setchar41 ("29) h:=15477767+254870=15732637, hh:=998 +661: setchar46 ("2E) h:=15732637+182043=15914680, hh:=1010 +[).] +662: pop +level 1:(h=0,v=11665408,w=0,x=0,y=786432,z=0,hh=0,vv=739) +663: y0 786432 v:=11665408+786432=12451840, vv:=789 +664: push +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +665: push +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +666: fntnum1 current font is min10 +667: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +670: pop +level 2:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +671: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +675: fntnum0 current font is ec-lmr10 +676: setchar99 ("63) h:=630598+291275=921873, hh:=58 +677: setchar111 ("6F) h:=921873+327680=1249553, hh:=79 +678: setchar110 ("6E) h:=1249553+364085=1613638, hh:=102 +679: right2 -18205 h:=1613638-18205=1595433, hh:=101 +682: setchar116 ("74) h:=1595433+254870=1850303, hh:=117 +683: setchar101 ("65) h:=1850303+291275=2141578, hh:=135 +684: setchar120 ("78) h:=2141578+345898=2487476, hh:=157 +685: setchar116 ("74) h:=2487476+254870=2742346, hh:=173 +686: setchar115 ("73) h:=2742346+258506=3000852, hh:=189 +687: setchar46 ("2E) h:=3000852+182043=3182895, hh:=201 +[contexts.] +688: pop +level 1:(h=0,v=12451840,w=0,x=0,y=786432,z=0,hh=0,vv=789) +689: y0 786432 v:=12451840+786432=13238272, vv:=839 +690: push +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +691: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +695: fntnum1 current font is min10 +696: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[␢] +699: fntnum0 current font is ec-lmr10 +700: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +702: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +704: w3 218453 h:=2924358+218453=3142811, hh:=199 +708: setchar97 ("61) h:=3142811+327680=3470491, hh:=220 +[ a] +709: set1 197("C5) h:=3470491+491520=3962011, hh:=251 +711: set1 191("BF) h:=3962011+491520=4453531, hh:=282 +713: w0 218453 h:=4453531+218453=4671984, hh:=296 +[ ] +714: set1 197("C5) h:=4671984+491520=5163504, hh:=327 +716: set1 191("BF) h:=5163504+491520=5655024, hh:=358 +718: w0 218453 h:=5655024+218453=5873477, hh:=372 +[ ] +719: fntnum1 current font is min10 +720: set2 17727("453F) type=0 h:=5873477+630598=6504075, hh:=412 +723: w0 218453 h:=6504075+218453=6722528, hh:=426 +[䔿 ] +724: fntnum0 current font is ec-lmr10 +725: set1 197("C5) h:=6722528+491520=7214048, hh:=457 +727: set1 191("BF) h:=7214048+491520=7705568, hh:=488 +729: w0 218453 h:=7705568+218453=7924021, hh:=502 +[ ] +730: set1 197("C5) h:=7924021+491520=8415541, hh:=533 +732: set1 191("BF) h:=8415541+491520=8907061, hh:=564 +734: w0 218453 h:=8907061+218453=9125514, hh:=578 +[ ] +735: fntnum1 current font is min10 +736: set2 17727("453F) type=0 h:=9125514+630598=9756112, hh:=618 +739: x3 436906 h:=9756112+436906=10193018, hh:=646 +[䔿 ] +743: fntnum0 current font is ec-lmr10 +744: set1 197("C5) h:=10193018+491520=10684538, hh:=677 +746: set1 191("BF) h:=10684538+491520=11176058, hh:=708 +748: w0 218453 h:=11176058+218453=11394511, hh:=722 +[ ] +749: fntnum1 current font is min10 +750: set2 17727("453F) type=0 h:=11394511+630598=12025109, hh:=762 +753: x0 436906 h:=12025109+436906=12462015, hh:=789 +754: set2 9252("2424) type=0 h:=12462015+630598=13092613, hh:=829 +[䔿 ] +757: fntnum0 current font is ec-lmr10 +758: setchar109 ("6D) h:=13092613+546111=13638724, hh:=864 +759: setchar97 ("61) h:=13638724+327680=13966404, hh:=885 +760: setchar99 ("63) h:=13966404+291275=14257679, hh:=903 +761: setchar114 ("72) h:=14257679+256683=14514362, hh:=919 +762: setchar111 ("6F) h:=14514362+327680=14842042, hh:=940 +763: setchar58 ("3A) h:=14842042+182043=15024085, hh:=952 +764: setchar45 ("2D) h:=15024085+218453=15242538, hh:=966 +765: setchar62 ("3E) h:=15242538+509738=15752276, hh:=998 +[macro:->] +766: set1 197("C5) h:=15752276+491520=16243796, hh:=1029 +768: set1 191("BF) h:=16243796+491520=16735316, hh:=1060 +770: w0 218453 h:=16735316+218453=16953769, hh:=1074 +[ ] +771: set1 197("C5) h:=16953769+491520=17445289, hh:=1105 +773: set1 191("BF) h:=17445289+491520=17936809, hh:=1136 +775: w0 218453 h:=17936809+218453=18155262, hh:=1150 +[ ] +776: fntnum1 current font is min10 +777: set2 17727("453F) type=0 h:=18155262+630598=18785860, hh:=1190 +[䔿] +780: pop +level 1:(h=0,v=13238272,w=0,x=0,y=786432,z=0,hh=0,vv=839) +781: y0 786432 v:=13238272+786432=14024704, vv:=888 +782: push +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +783: right3 1310720 h:=0+1310720=1310720, hh:=83 +787: set2 9250("2422) type=0 h:=1310720+630598=1941318, hh:=123 +[ ␢] +790: fntnum0 current font is ec-lmr10 +791: set1 197("C5) h:=1941318+491520=2432838, hh:=154 +793: set1 191("BF) h:=2432838+491520=2924358, hh:=185 +795: w3 218453 h:=2924358+218453=3142811, hh:=199 +[ ] +799: set1 197("C5) h:=3142811+491520=3634331, hh:=230 +801: set1 191("BF) h:=3634331+491520=4125851, hh:=261 +803: fntnum1 current font is min10 +804: set2 17727("453F) type=0 h:=4125851+630598=4756449, hh:=301 +[䔿] +807: fntnum0 current font is ec-lmr10 +808: set1 197("C5) h:=4756449+491520=5247969, hh:=332 +810: set1 191("BF) h:=5247969+491520=5739489, hh:=363 +812: w0 218453 h:=5739489+218453=5957942, hh:=377 +813: setchar97 ("61) h:=5957942+327680=6285622, hh:=398 +[ a] +814: set1 197("C5) h:=6285622+491520=6777142, hh:=429 +816: set1 191("BF) h:=6777142+491520=7268662, hh:=460 +818: w0 218453 h:=7268662+218453=7487115, hh:=474 +[ ] +819: set1 197("C5) h:=7487115+491520=7978635, hh:=505 +821: set1 191("BF) h:=7978635+491520=8470155, hh:=536 +823: w0 218453 h:=8470155+218453=8688608, hh:=550 +[ ] +824: fntnum1 current font is min10 +825: set2 17727("453F) type=0 h:=8688608+630598=9319206, hh:=590 +828: w0 218453 h:=9319206+218453=9537659, hh:=604 +[䔿 ] +829: fntnum0 current font is ec-lmr10 +830: set1 197("C5) h:=9537659+491520=10029179, hh:=635 +832: set1 191("BF) h:=10029179+491520=10520699, hh:=666 +834: w0 218453 h:=10520699+218453=10739152, hh:=680 +[ ] +835: set1 197("C5) h:=10739152+491520=11230672, hh:=711 +837: set1 191("BF) h:=11230672+491520=11722192, hh:=742 +839: w0 218453 h:=11722192+218453=11940645, hh:=756 +[ ] +840: fntnum1 current font is min10 +841: set2 17727("453F) type=0 h:=11940645+630598=12571243, hh:=796 +844: right3 436906 h:=12571243+436906=13008149, hh:=824 +[䔿 ] +848: fntnum0 current font is ec-lmr10 +849: set1 197("C5) h:=13008149+491520=13499669, hh:=855 +851: set1 191("BF) h:=13499669+491520=13991189, hh:=886 +853: w0 218453 h:=13991189+218453=14209642, hh:=900 +[ ] +854: fntnum1 current font is min10 +855: set2 17727("453F) type=0 h:=14209642+630598=14840240, hh:=940 +858: w0 218453 h:=14840240+218453=15058693, hh:=954 +859: set2 9252("2424) type=0 h:=15058693+630598=15689291, hh:=994 +[䔿 ] +862: fntnum0 current font is ec-lmr10 +863: set1 255("FF) h:=15689291+327680=16016971, hh:=1015 +865: pop +level 1:(h=0,v=14024704,w=0,x=0,y=786432,z=0,hh=0,vv=888) +866: y0 786432 v:=14024704+786432=14811136, vv:=938 +867: push +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +868: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +872: fntnum1 current font is min10 +873: set2 9252("2424) type=0 h:=1310720+630598=1941318, hh:=123 +[] +876: pop +level 1:(h=0,v=14811136,w=0,x=0,y=786432,z=0,hh=0,vv=938) +877: y0 786432 v:=14811136+786432=15597568, vv:=988 +878: push +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +879: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +883: fntnum0 current font is ec-lmr10 +884: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +885: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +886: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +887: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +888: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +889: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +890: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +891: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +892: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +893: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[.a...c.b..] +894: pop +level 1:(h=0,v=15597568,w=0,x=0,y=786432,z=0,hh=0,vv=988) +895: y0 786432 v:=15597568+786432=16384000, vv:=1038 +896: push +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +897: right3 1310720 h:=0+1310720=1310720, hh:=83 +901: setchar46 ("2E) h:=1310720+182043=1492763, hh:=95 +902: setchar97 ("61) h:=1492763+327680=1820443, hh:=116 +903: setchar46 ("2E) h:=1820443+182043=2002486, hh:=128 +904: setchar46 ("2E) h:=2002486+182043=2184529, hh:=140 +905: setchar46 ("2E) h:=2184529+182043=2366572, hh:=152 +906: setchar99 ("63) h:=2366572+291275=2657847, hh:=170 +907: setchar46 ("2E) h:=2657847+182043=2839890, hh:=182 +908: setchar98 ("62) h:=2839890+364085=3203975, hh:=205 +909: setchar46 ("2E) h:=3203975+182043=3386018, hh:=216 +910: setchar46 ("2E) h:=3386018+182043=3568061, hh:=228 +[ .a...c.b..] +911: pop +level 1:(h=0,v=16384000,w=0,x=0,y=786432,z=0,hh=0,vv=1038) +912: y0 786432 v:=16384000+786432=17170432, vv:=1088 +913: push +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +914: push +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +915: fntnum1 current font is min10 +916: set2 8739("2223) type=0 h:=0+630598=630598, hh:=40 +[∣] +919: pop +level 2:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +920: right3 630598 h:=0+630598=630598, hh:=40 +[ ] +924: fntnum0 current font is ec-lmr10 +925: setchar99 ("63) h:=630598+291275=921873, hh:=58 +926: setchar115 ("73) h:=921873+258506=1180379, hh:=74 +927: setchar110 ("6E) h:=1180379+364085=1544464, hh:=97 +928: setchar97 ("61) h:=1544464+327680=1872144, hh:=118 +929: setchar109 ("6D) h:=1872144+546111=2418255, hh:=153 +930: setchar101 ("65) h:=2418255+291275=2709530, hh:=171 +931: setchar50 ("32) h:=2709530+327680=3037210, hh:=192 +932: setchar46 ("2E) h:=3037210+182043=3219253, hh:=204 +[csname2.] +933: pop +level 1:(h=0,v=17170432,w=0,x=0,y=786432,z=0,hh=0,vv=1088) +934: y0 786432 v:=17170432+786432=17956864, vv:=1137 +935: push +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +936: right3 1310720 h:=0+1310720=1310720, hh:=83 +940: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +941: setchar72 ("48) h:=1674805+491520=2166325, hh:=137 +942: setchar79 ("4F) h:=2166325+509738=2676063, hh:=169 +943: right2 -18205 h:=2676063-18205=2657858, hh:=168 +946: setchar87 ("57) h:=2657858+673578=3331436, hh:=211 +947: right3 218453 h:=3331436+218453=3549889, hh:=225 +951: setchar46 ("2E) h:=3549889+182043=3731932, hh:=237 +952: setchar46 ("2E) h:=3731932+182043=3913975, hh:=249 +953: setchar46 ("2E) h:=3913975+182043=4096018, hh:=261 +[ SHOW ...] +954: pop +level 1:(h=0,v=17956864,w=0,x=0,y=786432,z=0,hh=0,vv=1137) +955: y0 786432 v:=17956864+786432=18743296, vv:=1187 +956: push +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +957: right3 1310720 h:=0+1310720=1310720, hh:=83 +961: setchar83 ("53) h:=1310720+364085=1674805, hh:=106 +962: setchar84 ("54) h:=1674805+473301=2148106, hh:=136 +963: setchar82 ("52) h:=2148106+482410=2630516, hh:=167 +964: setchar73 ("49) h:=2630516+236650=2867166, hh:=182 +965: setchar78 ("4E) h:=2867166+491520=3358686, hh:=213 +966: setchar71 ("47) h:=3358686+514261=3872947, hh:=246 +967: right3 218453 h:=3872947+218453=4091400, hh:=259 +971: setchar92 ("5C) h:=4091400+327680=4419080, hh:=280 +[ STRING \] +972: set1 197("C5) h:=4419080+491520=4910600, hh:=311 +974: set1 191("BF) h:=4910600+491520=5402120, hh:=342 +976: setchar46 ("2E) h:=5402120+182043=5584163, hh:=354 +977: setchar92 ("5C) h:=5584163+327680=5911843, hh:=375 +[.\] +978: fntnum1 current font is min10 +979: set2 17727("453F) type=0 h:=5911843+630598=6542441, hh:=415 +[䔿] +982: fntnum0 current font is ec-lmr10 +983: setchar46 ("2E) h:=6542441+182043=6724484, hh:=427 +984: setchar92 ("5C) h:=6724484+327680=7052164, hh:=448 +[.\] +985: set1 197("C5) h:=7052164+491520=7543684, hh:=479 +987: set1 191("BF) h:=7543684+491520=8035204, hh:=510 +989: setchar46 ("2E) h:=8035204+182043=8217247, hh:=522 +990: setchar92 ("5C) h:=8217247+327680=8544927, hh:=543 +[.\] +991: fntnum1 current font is min10 +992: set2 17727("453F) type=0 h:=8544927+630598=9175525, hh:=583 +[䔿] +995: pop +level 1:(h=0,v=18743296,w=0,x=0,y=786432,z=0,hh=0,vv=1187) +996: y0 786432 v:=18743296+786432=19529728, vv:=1237 +997: push +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +998: right3 1310720 h:=0+1310720=1310720, hh:=83 +[ ] +1002: fntnum0 current font is ec-lmr10 +1003: setchar77 ("4D) h:=1310720+600768=1911488, hh:=121 +1004: setchar69 ("45) h:=1911488+446005=2357493, hh:=149 +1005: setchar65 ("41) h:=2357493+491520=2849013, hh:=180 +1006: setchar78 ("4E) h:=2849013+491520=3340533, hh:=211 +1007: setchar73 ("49) h:=3340533+236650=3577183, hh:=226 +1008: setchar78 ("4E) h:=3577183+491520=4068703, hh:=257 +1009: setchar71 ("47) h:=4068703+514261=4582964, hh:=290 +1010: right3 218453 h:=4582964+218453=4801417, hh:=304 +1014: setchar109 ("6D) h:=4801417+546111=5347528, hh:=339 +1015: setchar97 ("61) h:=5347528+327680=5675208, hh:=360 +1016: setchar99 ("63) h:=5675208+291275=5966483, hh:=378 +1017: setchar114 ("72) h:=5966483+256683=6223166, hh:=394 +1018: setchar111 ("6F) h:=6223166+327680=6550846, hh:=415 +1019: setchar58 ("3A) h:=6550846+182043=6732889, hh:=427 +1020: setchar45 ("2D) h:=6732889+218453=6951342, hh:=441 +1021: setchar62 ("3E) h:=6951342+509738=7461080, hh:=473 +1022: setchar97 ("61) h:=7461080+327680=7788760, hh:=494 +1023: setchar46 ("2E) h:=7788760+182043=7970803, hh:=506 +1024: setchar109 ("6D) h:=7970803+546111=8516914, hh:=541 +1025: setchar97 ("61) h:=8516914+327680=8844594, hh:=562 +1026: setchar99 ("63) h:=8844594+291275=9135869, hh:=580 +1027: setchar114 ("72) h:=9135869+256683=9392552, hh:=596 +1028: setchar111 ("6F) h:=9392552+327680=9720232, hh:=617 +1029: setchar58 ("3A) h:=9720232+182043=9902275, hh:=629 +1030: setchar45 ("2D) h:=9902275+218453=10120728, hh:=643 +1031: setchar62 ("3E) h:=10120728+509738=10630466, hh:=675 +1032: setchar98 ("62) h:=10630466+364085=10994551, hh:=698 +1033: setchar46 ("2E) h:=10994551+182043=11176594, hh:=710 +1034: setchar109 ("6D) h:=11176594+546111=11722705, hh:=745 +1035: setchar97 ("61) h:=11722705+327680=12050385, hh:=765 +1036: setchar99 ("63) h:=12050385+291275=12341660, hh:=783 +1037: setchar114 ("72) h:=12341660+256683=12598343, hh:=799 +1038: setchar111 ("6F) h:=12598343+327680=12926023, hh:=820 +1039: setchar58 ("3A) h:=12926023+182043=13108066, hh:=832 +1040: setchar45 ("2D) h:=13108066+218453=13326519, hh:=846 +1041: setchar62 ("3E) h:=13326519+509738=13836257, hh:=878 +1042: setchar97 ("61) h:=13836257+327680=14163937, hh:=899 +1043: setchar46 ("2E) h:=14163937+182043=14345980, hh:=911 +1044: setchar109 ("6D) h:=14345980+546111=14892091, hh:=945 +1045: setchar97 ("61) h:=14892091+327680=15219771, hh:=966 +1046: setchar99 ("63) h:=15219771+291275=15511046, hh:=984 +1047: setchar114 ("72) h:=15511046+256683=15767729, hh:=1000 +1048: setchar111 ("6F) h:=15767729+327680=16095409, hh:=1021 +1049: setchar58 ("3A) h:=16095409+182043=16277452, hh:=1033 +1050: setchar45 ("2D) h:=16277452+218453=16495905, hh:=1047 +1051: setchar62 ("3E) h:=16495905+509738=17005643, hh:=1079 +1052: setchar98 ("62) h:=17005643+364085=17369728, hh:=1102 +1053: setchar46 ("2E) h:=17369728+182043=17551771, hh:=1114 +[MEANING macro:->a.macro:->b.macro:->a.macro:->b.] +1054: pop +level 1:(h=0,v=19529728,w=0,x=0,y=786432,z=0,hh=0,vv=1237) +1055: pop +level 0:(h=0,v=42152922,w=0,x=0,y=0,z=0,hh=0,vv=2670) +1056: down3 1572864 v:=42152922+1572864=43725786, vv:=2770 +1060: eop diff --git a/Build/source/texk/web2c/ptexdir/tests/pknj-e.log b/Build/source/texk/web2c/ptexdir/tests/pknj-e.log new file mode 100644 index 00000000000..4f42dab30bb --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/pknj-e.log @@ -0,0 +1,223 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**printkanji +(./printkanji.tex +\openout1 = `p-pknj-e.out'. + + +{MSG ^^c5^^bf ^^c5^^bf 顛 } +{TOT ^^c5^^bf ^^c5^^bf 顛 } +> \A=macro: +->^^c5^^bf ^^c5^^bf 顛 . +l.29 \show\A + + +■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +> \B=macro: +->macro:->^^c5^^bf ^^c5^^bf 顛 . +l.40 \show\B + + +■\jobname. {MSG *あ*p-pknj-e*:p-pknj-e:*macro:->:p-pknj-e:*} +{TOT *あ*p-pknj-e*:p-pknj-e:*macro:->:p-pknj-e:*} + +■oneletter. {MSG \catcode`\^^c5= 197} +{TOT \catcode`\^^c5= 197} +{MSG \catcode`^^c5 = 197} +{TOT \catcode`^^c5 = 197} +{MSG \catcode`^^c5^^bf = 197} +{TOT \catcode`^^c5^^bf = 197} +{MSG \catcode`顛 = 50623} +{TOT \catcode`顛 = 50623} +■csname1. +> \顛=macro: +->hoge. +l.72 \show\顛 + + +> \^^c5^^bf=macro: +->piyo. +l.73 \show\^^c5^^bf + + +> \^^c5^^bf=macro: +->piyo. +<recently read> \^^c5^^bf + +l.74 \expandafter\show\csname ^^c5^^bf\endcsname + + +{MSG \顛=>macro:->hoge} +{TOT \顛=>macro:->hoge} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} +{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} + +{MSG \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. } +{TOT \あ=> hoge, \^^a4^^a2=> piyo. \^^a4^^a2=> piyo. } +■0xFF. +{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +■contexts. +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1 + \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ... +l.136 \+\a + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a + \^^ff ^^ff\^^c5^^bf +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5 + \^^ff\^^c4\^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4 + \^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4\^^fe + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +\^^c5^^bf\^^ff^^c5 +! Undefined control sequence. +\b ->\^^c5^^be + .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd + ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 + . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be + .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd + ..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼 + . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +■csname2. +> \^^c5^^bf=macro: +->a. +l.162 SHOW \show\^^c5^^bf + .\show\顛.% + +> \顛=macro: +->b. +l.162 SHOW \show\^^c5^^bf.\show\顛 + .% + +> \^^c5^^bf=macro: +->a. +<recently read> \^^c5^^bf + +l.163 \expandafter\show\csname ^^c5^^bf\endcsname + .% + +> \顛=macro: +->b. +<recently read> \顛 + +l.164 \expandafter\show\csname 顛\endcsname + + +\^^e3^^81^^82\あ [50623] +Memory usage before: 1270&5512; after: 118&5077; still untouched: 4992459 + ) +Here is how much of TeX's memory you used: + 25 strings out of 497935 + 137 string characters out of 6219478 + 7541 words of memory out of 5000000 + 969 multiletter control sequences out of 15000+600000 + 3271 words of font info for 2 fonts, out of 8000000 for 9000 + 0 hyphenation exceptions out of 8191 + 6i,4n,2p,97b,22s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on p-pknj-e.dvi (1 page, 1140 bytes). diff --git a/Build/source/texk/web2c/ptexdir/tests/pknj-s.log b/Build/source/texk/web2c/ptexdir/tests/pknj-s.log new file mode 100644 index 00000000000..7f753e894b5 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/pknj-s.log @@ -0,0 +1,223 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**printkanji +(./printkanji.tex +\openout1 = `p-pknj-s.out'. + + +{MSG ^^c5^^bf ^^c5^^bf 顛 } +{TOT ^^c5^^bf ^^c5^^bf 顛 } +> \A=macro: +->^^c5^^bf ^^c5^^bf 顛 . +l.29 \show\A + + +■\meaning. {MSG macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +{TOT macro:->macro:->^^c5^^bf ^^c5^^bf 顛 } +> \B=macro: +->macro:->^^c5^^bf ^^c5^^bf 顛 . +l.40 \show\B + + +■\jobname. {MSG *あ*p-pknj-s*:p-pknj-s:*macro:->:p-pknj-s:*} +{TOT *あ*p-pknj-s*:p-pknj-s:*macro:->:p-pknj-s:*} + +■oneletter. {MSG \catcode`\^^c5= 197} +{TOT \catcode`\^^c5= 197} +{MSG \catcode`^^c5 = 197} +{TOT \catcode`^^c5 = 197} +{MSG \catcode`^^c5^^bf = 197} +{TOT \catcode`^^c5^^bf = 197} +{MSG \catcode`顛 = 37726} +{TOT \catcode`顛 = 37726} +■csname1. +> \顛=macro: +->hoge. +l.72 \show\顛 + + +> \^^c5^^bf=macro: +->piyo. +l.73 \show\^^c5^^bf + + +> \^^c5^^bf=macro: +->piyo. +<recently read> \^^c5^^bf + +l.74 \expandafter\show\csname ^^c5^^bf\endcsname + + +{MSG \顛=>macro:->hoge} +{TOT \顛=>macro:->hoge} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf=>macro:->piyo} +{TOT \^^c5^^bf=>macro:->piyo} +{MSG \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} +{TOT \^^c5^^bf, \^^c5^^bf, \^^c5^^bf, \顛.} + +{MSG \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. } +{TOT \あ=> hoge, \^^e3^^81^^82=> piyo. \^^e3^^81^^82=> piyo. } +■0xFF. +{MSG あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{TOT あ, ^^c5^^bf ^^c5^^bf 顛 , \^^ff^^c5^^ff^^bf, macro:->(\^^ff^^c5^^ff^^bf:m +acro:->あ).} +{MSG い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +{TOT い, (\^^ff:macro:->い), \^^ff, macro:->(\^^ff:macro:->い).} +■contexts. +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +\+#1->\^^c5^^bf \顛 \^^ff^^c5^^ff^^bf #1 + \^^ff macro:->^^c5^^bf ^^c5^^bf 顛 ... +l.136 \+\a + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\@->\A \undefined + +\a ->^^c5^^bf a^^c5^^bf ^^c5^^bf 顛 \@ + ^^c5^^bf 顛 +l.138 ...5^^ff^^bf ^^c5^^bf \^^c5^^bf ^^c5^^bf顛\a + \^^ff ^^ff\^^c5^^bf +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5 + \^^ff\^^c4\^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4 + \^^fe +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.140 \^^c5\^^ff\^^c4\^^fe + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +\^^c5^^bf\^^ff^^c5 +! Undefined control sequence. +\b ->\^^c5^^be + .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ->\^^c5^^be .\^^c5^^bf .\^^c5^^bd + ..\転 .\顛 .\貼 . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +\b ...5^^be .\^^c5^^bf .\^^c5^^bd ..\転 .\顛 .\貼 + . +l.154 \b + +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be + .\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd + ..\転.\顛.\貼. +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +! Undefined control sequence. +l.156 \^^c5^^be.\^^c5^^bf.\^^c5^^bd..\転.\顛.\貼 + . +The control sequence at the end of the top line +of your error message was never \def'ed. If you have +misspelled it (e.g., `\hobx'), type `I' and the correct +spelling (e.g., `I\hbox'). Otherwise just continue, +and I'll forget about whatever was undefined. + +■csname2. +> \^^c5^^bf=macro: +->a. +l.162 SHOW \show\^^c5^^bf + .\show\顛.% + +> \顛=macro: +->b. +l.162 SHOW \show\^^c5^^bf.\show\顛 + .% + +> \^^c5^^bf=macro: +->a. +<recently read> \^^c5^^bf + +l.163 \expandafter\show\csname ^^c5^^bf\endcsname + .% + +> \顛=macro: +->b. +<recently read> \顛 + +l.164 \expandafter\show\csname 顛\endcsname + + +\^^e3^^81^^82\あ [37726] +Memory usage before: 1274&5515; after: 118&5078; still untouched: 4992456 + ) +Here is how much of TeX's memory you used: + 24 strings out of 497935 + 135 string characters out of 6219475 + 7544 words of memory out of 5000000 + 968 multiletter control sequences out of 15000+600000 + 3271 words of font info for 2 fonts, out of 8000000 for 9000 + 0 hyphenation exceptions out of 8191 + 6i,4n,2p,97b,22s stack positions out of 10000i,1000n,20000p,200000b,200000s + +Output written on p-pknj-s.dvi (1 page, 1148 bytes). diff --git a/Build/source/texk/web2c/ptexdir/tests/printkanji.tex b/Build/source/texk/web2c/ptexdir/tests/printkanji.tex index a8f1d4cb5a0..d176d9a53bf 100644 --- a/Build/source/texk/web2c/ptexdir/tests/printkanji.tex +++ b/Build/source/texk/web2c/ptexdir/tests/printkanji.tex @@ -2,7 +2,16 @@ \tracingstats=1000 \noautoxspacing \newlinechar=`\^^J -\font\x=ec-lmtt10 \x +\font\x=ec-lmr10 \x +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi + + \immediate\openout1=\jobname.out \def\MSG#1{% \message{\string{MSG #1\string}}% @@ -135,7 +144,6 @@ A: \A \catcode`\^^bf=11 \catcode`\^^bd=11 \catcode`\^^ff=11 -\font\x=ec-lmtt10 \x\scrollmode \def\^^c5^^bf{a} \def\顛{b} diff --git a/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex b/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex index 5dce40d2e12..cf38ff5e8d6 100644 --- a/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex +++ b/Build/source/texk/web2c/ptexdir/tests/ptexlineendmode.tex @@ -1,4 +1,12 @@ -\font\fn=ec-lmr10\fn\catcode`\^^9f=11 +\font\fn=ec-lmr10\fn +\ifx\kanjiskip\undefined\else + \ifnum\jis"2121="3000 + \jfont\jpy=umin10 at 10pt\jpy + \else + \jfont\jpy=min10 at 10pt\jpy + \fi +\fi +\catcode`\^^9f=11 \def\◆{◇}\def\漢{◇}\def\^^9f{S} \ptexlineendmode=0 % p4.0.0標準 diff --git a/Build/source/texk/web2c/ptexdir/tests/ucs-e.log b/Build/source/texk/web2c/ptexdir/tests/ucs-e.log new file mode 100644 index 00000000000..33428e2db9e --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/ucs-e.log @@ -0,0 +1,28 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**p-ucs +(./p-ucs.tex +<EUC> +\ucs. +\cntA=\count26 +162: 41457 +163: 41458 +165: 41455 +167: 41464 +168: 41391 +172: 41676 +176: 41451 +177: 41438 +180: 41389 +182: 41721 +215: 41439 +247: 41440 +\toucs. +-1, -1, -1, -1, -1 +-1, 34821, 34722 +12354, 29081, -1 +41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676 +41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676 +41393, 41405, 41409, 41410, 41412, 41437, 41455, 41457, 41458, 41676 + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/ucs-s.log b/Build/source/texk/web2c/ptexdir/tests/ucs-s.log new file mode 100644 index 00000000000..576b027e823 --- /dev/null +++ b/Build/source/texk/web2c/ptexdir/tests/ucs-s.log @@ -0,0 +1,28 @@ + restricted \write18 enabled. + %&-line parsing enabled. +**p-ucs +(./p-ucs.tex +<SJIS> +\ucs. +\cntA=\count26 +162: 33169 +163: 33170 +165: 33167 +167: 33176 +168: 33102 +172: 33226 +176: 33163 +177: 33149 +180: 33100 +182: 33271 +215: 33150 +247: 33152 +\toucs. +-1, -1, -1, -1, -1 +12450, 29081, -1 +-1, -1, -1 +33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226 +33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226 +33104, 33116, 33120, 33121, 33123, 33148, 33167, 33169, 33170, 33226 + ) +No pages of output. diff --git a/Build/source/texk/web2c/ptexdir/tests/ucs.tex b/Build/source/texk/web2c/ptexdir/tests/ucs.tex index de8a32fc1df..520a7485d57 100644 --- a/Build/source/texk/web2c/ptexdir/tests/ucs.tex +++ b/Build/source/texk/web2c/ptexdir/tests/ucs.tex @@ -9,7 +9,8 @@ \typeout{\string\ucs.} \newcount\cntA \loop\ifnum\cntA<256 - \ifnum0>\ucs\cntA\else\typeout{\the\cntA: \ucs\cntA (\Uchar\ucs\cntA)}\fi + \ifnum0>\ucs\cntA\else\typeout{\the\cntA: \ucs\cntA % + \ifx\Uchar\undefined\else(\Uchar\ucs\cntA)\fi}\fi \advance\cntA1 \repeat @@ -23,6 +24,12 @@ \typeout{\toucs"0, \toucs"80, \toucs"D7, \toucs"A7, \toucs"10000} \typeout{\toucs"8341, \toucs"EAA4, \toucs"EAA5} \typeout{\toucs"A4A2, \toucs"F4A6, \toucs"F4A7} +\typeout{\jis"2131, \jis"213D, \jis"2141, \jis"2142, \jis"2144, + \jis"215D, \jis"216F, \jis"2171, \jis"2172, \jis"224C} +\typeout{\ucs"203E, \ucs"2014, \ucs"301C, \ucs"2016, \ucs"2026, + \ucs"2212, \ucs"00A5, \ucs"00A2, \ucs"00A3, \ucs"00AC} +\typeout{\ucs"FFE3, \ucs"2015, \ucs"FF5E, \ucs"2225, \ucs"22EF, + \ucs"FF0D, \ucs"FFE5, \ucs"FFE0, \ucs"FFE1, \ucs"FFE2} \bye diff --git a/Build/source/texk/web2c/showstream.ch b/Build/source/texk/web2c/showstream.ch index d0347a795c4..17a6a70e401 100644 --- a/Build/source/texk/web2c/showstream.ch +++ b/Build/source/texk/web2c/showstream.ch @@ -72,7 +72,7 @@ show_lists_code: begin begin_diagnostic; show_activities; end; @y show_lists_code: - begin @<Adjust |selector| based on |show_stream|@> + begin @<Adjust |selector| based on |show_stream|@>@; begin_diagnostic; show_activities; end; @z @@ -132,13 +132,9 @@ While the other cases have separate blocks @ @<Show the current meaning of a token...@>= begin get_token; @y -@ @<Adjust |selector| based on |show_stream|@>= -if (show_stream>=0) and (show_stream<no_print) and write_open[show_stream] then - selector:=show_stream; - @ @<Show the current meaning of a token...@>= begin get_token; - @<Adjust |selector| based on |show_stream|@> + @<Adjust |selector| based on |show_stream|@>@; @z @x @@ -147,7 +143,7 @@ begin scan_register_num; fetch_box(p); begin_diagnostic; @y @ @<Show the current contents of a box@>= begin scan_register_num; fetch_box(p); -@<Adjust |selector| based on |show_stream|@> +@<Adjust |selector| based on |show_stream|@>@; begin_diagnostic; @z @@ -157,19 +153,29 @@ begin p:=the_toks; @y @ @<Show the current value of some parameter...@>= begin p:=the_toks; -@<Adjust |selector| based on |show_stream|@> +@<Adjust |selector| based on |show_stream|@>@; @z @x show_groups: begin begin_diagnostic; show_save_groups; @y -show_groups: begin @<Adjust |selector| based on |show_stream|@> +show_groups: begin @<Adjust |selector| based on |show_stream|@>@; begin_diagnostic; show_save_groups; @z @x show_ifs: begin begin_diagnostic; print_nl(""); print_ln; @y -show_ifs: begin @<Adjust |selector| based on |show_stream|@> +show_ifs: begin @<Adjust |selector| based on |show_stream|@>@; begin_diagnostic; print_nl(""); print_ln; @z + +@x +@* \[55] Index. +@y +@ @<Adjust |selector| based on |show_stream|@>= +if (show_stream>=0) and (show_stream<no_print) and write_open[show_stream] then + selector:=show_stream; + +@* \[55] Index. +@z diff --git a/Build/source/texk/web2c/tangle.ch b/Build/source/texk/web2c/tangle.ch index 5bb61fe776e..acf8986301e 100644 --- a/Build/source/texk/web2c/tangle.ch +++ b/Build/source/texk/web2c/tangle.ch @@ -73,7 +73,7 @@ procedure initialize; var @<Local variables for initialization@>@/ begin @<Set initial values@>@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; var @<Local variables for initialization@>@/ begin @@ -302,6 +302,12 @@ the replacement text. chopped_id[s]:=buffer[i]+@'40 @z +@x [6.61] l.1086 +else @<Define and output a new string of the pool@>; +@y +else @<Define \(and output a new string of the pool@>; +@z + @x [6.63] l.1102 - Case smashing options/strict checking. if c<>"_" then begin if c>="a" then c:=c-@'40; {merge lowercase with uppercase} @@ -321,7 +327,7 @@ if l-double_chars=2 then {this string is for a single character} else begin equiv[p]:=string_ptr+@'100000; l:=l-double_chars-1; @y -@<Define and output a new string...@>= +@<Define \(and output a new string...@>= begin ilk[p]:=numeric; {strings are like numeric macros} if l-double_chars=2 then {this string is for a single character} equiv[p]:=buffer[id_first+1]+@'10000000000 @@ -681,7 +687,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 9; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/tests/dump-basic.tex b/Build/source/texk/web2c/tests/dump-basic.tex new file mode 100644 index 00000000000..209f3630b56 --- /dev/null +++ b/Build/source/texk/web2c/tests/dump-basic.tex @@ -0,0 +1,6 @@ +\ifx\fmtname\undefined + \input basic + \def\fmtname{basic} + \expandafter\dump +\fi +\end diff --git a/Build/source/texk/web2c/tests/ec-lmr10.tfm b/Build/source/texk/web2c/tests/ec-lmr10.tfm Binary files differnew file mode 100644 index 00000000000..719e4f082e8 --- /dev/null +++ b/Build/source/texk/web2c/tests/ec-lmr10.tfm diff --git a/Build/source/texk/web2c/texprofdir/ChangeLog b/Build/source/texk/web2c/texprofdir/ChangeLog new file mode 100644 index 00000000000..a4079b33063 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/ChangeLog @@ -0,0 +1,16 @@ +2024-09-07 Karl Berry <karl@freefriends.org> + + * am/texprof.am (texprofile_c): depend on texprofile-tangle, + not texprof-tangle. + (nodist_texprof_SOURCES): not needed, since these programs + have one source file; and texprof does not depend on texprofile. + +2024-09-02 Martin Ruckert <martin.ruckert@hm.edu> + + Adding the texprofile manual page + +2024-09-02 Martin Ruckert <martin.ruckert@hm.edu> + + Initial commit + +(This file public domain.) diff --git a/Build/source/texk/web2c/texprofdir/README.TXT b/Build/source/texk/web2c/texprofdir/README.TXT new file mode 100644 index 00000000000..d099b850f13 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/README.TXT @@ -0,0 +1,5 @@ +TeXprof is an extended version of TeX that collects run time data for the +TeXprofile program. + +TeXprof and TeXprofile together implement profiling for TeX input files. + diff --git a/Build/source/texk/web2c/texprofdir/am/texprof.am b/Build/source/texk/web2c/texprofdir/am/texprof.am new file mode 100644 index 00000000000..6b5ae25f3e5 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/am/texprof.am @@ -0,0 +1,76 @@ +## $Id$ +## texk/web2c/texprofdir/am/texprof.am: Makefile fragment for TeXprof +## +## Copyright 2024 Martin Ruckert <ruckert@cs.hm.edu> +## You may freely use, modify and/or distribute this file. + +if TEXPROF +bin_PROGRAMS += texprof texprofile + +# convenience target to build all the programs here. +texprofall: texprof texprofile + +# See hitex.am for comments about handling man. +man1_MANS = texprofdir/man/texprof.man texprofdir/man/texprofile.man +endif TEXPROF + +texprof_CPPFLAGS = $(AM_CPPFLAGS) +texprof_CPPFLAGS += -I$(srcdir)/libmd5 +texprof_CPPFLAGS += -DINIT -DSTAT -DWEB2CVERSION="\"${WEB2CVERSION}\"" +# -DDEBUG drops into an interactive debugging session. +texprof_LDADD = $(KPATHSEA_LIBS) libmd5.a +texprof_DEPENDENCIES = $(KPATHSEA_DEPEND) libmd5.a +# +texprofile_CPPFLAGS = $(AM_CPPFLAGS) +texprofile_LDADD = +texprofile_DEPENDENCIES = +# +# TeXprof CWEB sources +texprof_web = texprofdir/texprof.w +texprofile_web = texprofdir/texprofile.w + +# Creating several files: need stamp file and two rules with identical recipes +texprof_ctangle_sh = CWEBINPUTS=$(srcdir)/texprofdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE) + +# For each cweb (.w) source file, list the files generated. +texprof_c = texprof.c +texprofile_c = texprofile.c + +$(texprof_c): texprof-tangle +$(texprofile_c): texprofile-tangle + +# +texprof-tangle: ctangle$(EXEEXT) texprofdir/texprof.w tangle-sh + $(texprof_ctangle_sh) texprof + +texprofile-tangle: ctangle$(EXEEXT) texprofdir/texprofile.w tangle-sh + $(texprof_ctangle_sh) texprofile + +EXTRA_DIST += texprofdir/ChangeLog $(texprof_web) + +DISTCLEANFILES += $(nodist_texprof_SOURCES) +DISTCLEANFILES += texprofile-tangle texprof-tangle + +## TeXprof tests +# +#texprof_tests = texprofdir/tests/hello.test \ +# texprofdir/tests/rule.test +# +#texprofdir/tests/hello.log: texprof$(EXEEXT) histretch$(EXEEXT) +#texprofdir/tests/rule.log: texprof$(EXEEXT) histretch$(EXEEXT) hishrink$(EXEEXT) +# +#EXTRA_DIST += $(texprof_tests) +# +#if TEXPROF +#TESTS += $(texprof_tests) +#endif TEXPROF +# +## hello.test +#EXTRA_DIST += texprofdir/tests/hello.tex +#DISTCLEANFILES += hello.log + +## rule.test +#EXTRA_DIST += texprofdir/tests/rule.tex +#DISTCLEANFILES += rule.log + +# (end of texprof.am) diff --git a/Build/source/texk/web2c/texprofdir/doc/texprof.hnt b/Build/source/texk/web2c/texprofdir/doc/texprof.hnt Binary files differnew file mode 100644 index 00000000000..fb3d7c162f1 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/doc/texprof.hnt diff --git a/Build/source/texk/web2c/texprofdir/doc/texprof.pdf b/Build/source/texk/web2c/texprofdir/doc/texprof.pdf Binary files differnew file mode 100644 index 00000000000..d07addfa31e --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/doc/texprof.pdf diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofile.hnt b/Build/source/texk/web2c/texprofdir/doc/texprofile.hnt Binary files differnew file mode 100644 index 00000000000..507675ef317 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/doc/texprofile.hnt diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofile.pdf b/Build/source/texk/web2c/texprofdir/doc/texprofile.pdf Binary files differnew file mode 100644 index 00000000000..eca97af5571 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/doc/texprofile.pdf diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofileman.hnt b/Build/source/texk/web2c/texprofdir/doc/texprofileman.hnt Binary files differnew file mode 100644 index 00000000000..8cd8af1aa55 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/doc/texprofileman.hnt diff --git a/Build/source/texk/web2c/texprofdir/doc/texprofileman.pdf b/Build/source/texk/web2c/texprofdir/doc/texprofileman.pdf Binary files differnew file mode 100644 index 00000000000..57e23386aa7 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/doc/texprofileman.pdf diff --git a/Build/source/texk/web2c/texprofdir/man/texprof.man b/Build/source/texk/web2c/texprofdir/man/texprof.man new file mode 100644 index 00000000000..b83047e9590 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/man/texprof.man @@ -0,0 +1,345 @@ +.TH TEXPROF 1 "2 September 2024" "Version 1.0" +.\"===================================================================== +.if n .ds MF Metafont +.if t .ds MF Metafont +.if t .ds TX \fRT\\h'-0.1667m'\\v'0.20v'E\\v'-0.20v'\\h'-0.125m'X\fP +.if n .ds TX TeX +.ie t .ds OX \fIT\v'+0.25m'E\v'-0.25m'X\fP +.el .ds OX TeX +.\" BX definition must follow TX so BX can use TX +.if t .ds BX \fRB\s-2IB\s0\fP\*(TX +.if n .ds BX BibTeX +.\" LX definition must follow TX so LX can use TX +.if t .ds LX \fRL\\h'-0.36m'\\v'-0.15v'\s-2A\s0\\h'-0.15m'\\v'0.15v'\fP\*(TX +.if n .ds LX LaTeX +.if t .ds AX \fRA\\h'-0.1667m'\\v'0.20v'M\\v'-0.20v'\\h'-0.125m'S\fP\*(TX +.if n .ds AX AmSTeX +.if t .ds AY \fRA\\h'-0.1667m'\\v'0.20v'M\\v'-0.20v'\\h'-0.125m'S\fP\*(LX +.if n .ds AY AmSLaTeX +.if n .ds WB Web +.if t .ds WB W\s-2EB\s0 +.\"===================================================================== +.SH NAME +texprof \- A profiler for TeX source files +.SH SYNOPSIS +.B texprof +.RI [ options ] +.RI [ \fB&\fPformat ] +.RI [ file | \fB\e\fPcommands ] +.\"===================================================================== +.SH DESCRIPTION +Run the \*(TX profiler on +.IR file , +usually creating +.IR file.dvi +and +.IR file.tprof . +If the +.IR file +argument has no extension, ".tex" will be appended to it. +Instead of a file name, a list of \*(TX commands can be given, the first +of which must start with a backslash. +With a +.BI & format +argument, the \*(TX profiler uses a different set of precompiled commands, +contained in +.IR format\fB.fmt\fP ; +it is usually better to use the +.B -fmt +.I format +option instead. +.PP +The \*(TX profiler is a version of \*(TX that measures the time +\*(TX needs to execute individual input lines or macros. +It writes the collected data to a file with extension +.IR .tprof . +A separate program, +.B texprofile +(see +.BR texprofile (1)), +is used to display the content of a +.IR .tprof +file in a readable form. + +.PP +The typical use of the \*(TX profiler is with pre generated formats. +The +.B texprof +command uses the equivalent of the plain \*(TX format, and the +.B latexprof +command uses the equivalent of the \*(LX format. To investigate +the timing of macros targeted to +.B pdftex +or +.B pdflatex, +you can use +.B pdftexprof +or +.B pdflatexprof. + +.PP +The \*(TX profiler's handling of its command-line arguments is similar +to that of the other \*(TX programs in the +.I web2c +implementation. + +.\"===================================================================== +.SH OPTIONS +This version of the \*(TX profiler understands the following command line +options. +.TP +.BI -cnf-line \ string +Parse +.I string +as a +.I texmf.cnf +configuration line. See the Kpathsea manual. +.TP +.B -etex +Enable the e-\*(TX extensions. This option is only effective in +combination with +.BR -ini . +See +.BR etex (1). +.TP +.B -file-line-error +Print error messages in the form +.I file:line:error +which is similar to the way many compilers format them. +.TP +.B -no-file-line-error +Disable printing error messages in the +.I file:line:error +style. +.TP +.BI -fmt \ format +Use +.I format +as the name of the format to be used, instead of the name by which +the \*(TX profiler was called or a +.I %& +line. +.TP +.B -help +Print help message and exit. +.TP +.B -ini +Start in +.I INI +mode, which is used to dump formats. The +.I INI +mode can be used for typesetting, but no format is preloaded, and +basic initializations like setting catcodes may be required. +.TP +.BI -interaction \ mode +Sets the interaction mode. The mode can be either +.IR batchmode , +.IR nonstopmode , +.IR scrollmode , +and +.IR errorstopmode . +The meaning of these modes is the same as that of the corresponding +\ecommands. +.TP +.BI -jobname \ name +Use +.I name +for the job name, instead of deriving it from the name of the input file. +.TP +.BI -kpathsea-debug \ bitmask +Sets path searching debugging flags according to the bitmask. See the +.I Kpathsea +manual for details. +.TP +.B -ltx +Enable the \*(LX extensions. This option is only effective in +combination with +.BR -ini . +See +.BR latex (1). +.TP +.BI -mktex \ fmt +Enable +.RI mktex fmt , +where +.I fmt +must be either +.IR tex , +.IR tfm , +.IR fmt , +or +.IR pk . +.TP +.BI -no-mktex \ fmt +Disable +.RI mktex fmt , +where +.I fmt +must be either +.IR tex , +.IR tfm , +.IR fmt , +or +.IR pk . +.TP +.BI -output-directory \ directory +Write output files in +.I directory +instead of the current directory. Look up input files in +.I directory +first, then along the normal search path. +.TP +.B -parse-first-line +If the first line of the main input file begins with +.I %& +parse it to look for a dump name. +.TP +.B -no-parse-first-line +Disable parsing of the first line of the main input file. +.TP +.B -pdf +Enable the simulation of common +.B pdftex +primitives. This option requires the +.BR -ini +option and implies the +.BR -ltx +option. +See +.BR pdftex (1). +.TP +.B -prof +Enable profiling as soon as \*(TX enters the main loop. If tis option +it not used, profiling needs to switched on using the \eprofileon +primitive. Profiling can be switched of with the \eprofileoff +primitive. +.TP +.BI -progname \ name +Pretend to be program +.IR name . +This affects both the format used and the search paths. +.TP +.B -version +Print version information and exit. +.\"===================================================================== +.SH ENVIRONMENT +See the Kpathsea library documentation (e.g., the `Path specifications' +node) for precise details of how the environment variables are used. +The +.B kpsewhich +utility can be used to query the values of the variables. +.PP +.TP +.B TEXMFOUTPUT +Normally, \*(TX puts its output files in the current directory. If +any output file cannot be opened there, it tries to open it in the +directory specified in the environment variable TEXMFOUTPUT. +There is no default value for that variable. For example, if you say +.I texprof paper +and the current directory is not writable and TEXMFOUTPUT has +the value +.IR /tmp , +\*(TX attempts to create +.I /tmp/paper.log , +.I /tmp/paper.dvi , +and +.I /tmp/paper.tprof . +TEXMFOUTPUT is also checked for input +files, as \*(TX often generates files that need to be subsequently +read; for input, no suffixes (such as ``.tex'') are added by default, +the input name is simply checked as given. +.TP +.B TEXINPUTS +Search path for +.I \einput +and +.I \eopenin +files. +This normally starts with ``.'', so +that user files are found before system files. An empty path +component will be replaced with the paths defined in the +.I texmf.cnf +file. For example, set TEXINPUTS to ".:/home/user/tex:" to prepend the +current directory and ``/home/user/tex'' to the standard search path. +.TP +.B TEXFORMATS +Search path for format files. +.TP +.B TFMFONTS +Search path for font metric +.RI ( .tfm ) +files. +.TP +.B SOURCE_DATE_EPOCH +If set, its value, taken to be in epoch-seconds, will be used for the +creation date and as the reference moment for the time related +primitives of \*(LX. This is useful for making reproducible builds. +.TP +.B FORCE_SOURCE_DATE +If set to the value "1", the time-related \*(TX primitives +.RI ( \eyear , +.IR \emonth , +.IR \eday , +.IR \etime ) +are also initialized from the value of SOURCE_DATE_EPOCH. This is not +recommended if there is any viable alternative. +.PP +Many, many more environment variables may be consulted related to path +searching. See the Kpathsea manual. +.\"===================================================================== +.SH FILES +The location of the files mentioned below varies from system to +system. Use the +.B kpsewhich +utility to find their locations. +.TP +.I *.tfm +Metric files for \*(TX's fonts. +.TP +.I *.fmt +Predigested \*(TX format files. +.TP +.I *.pk *.pfb +Font files used by \*(TX. +.\"===================================================================== +.SH NOTES +This manual page is not meant to be exhaustive. The complete +documentation for the \*(TX profiler can be found in +.IR texprof.pdf . +Further information can be found in the manual of the +.IR "Kpathsea library" . +.\"===================================================================== +.SH BUGS +This version of the \*(TX profiler does not implement all of the +primitives that pdftex provides, and further, it will not produce +the same side effects. +.\"===================================================================== +.SH AVAILABILITY +The \*(TX profiler should compile on a large variety of machine architectures +and operating systems. The function to obtain timing information id POSIX +specific. +.PP +The sources of the \*(TX profiler are hosted at +.BR https://github.com/ruckertm/HINT +.\"===================================================================== +.SH "SEE ALSO" +.BR texprofiler (1), +.BR tex (1), +.BR latex (1), +.BR pdftex (1), +.BR pdflatex (1), and +.BR kpsewhich (1). +.\"===================================================================== +.SH AUTHORS +The primary author of the \*(TX profiler is Martin Ruckert, +with e\*(TX extensions by Peter Breitenlohner, +\*(LX extensions by Thierry Laronde, +and +the kpathsearch library by Karl Berry. +.PP +\*(TX was designed by Donald E. Knuth, +who implemented it using his \*(WB system for Pascal programs. +.PP +Many, many more contributed to the typesetting system now known as \*(TX; +far too many to name all of them here. +.\" vim: syntax=nroff diff --git a/Build/source/texk/web2c/texprofdir/man/texprofile.man b/Build/source/texk/web2c/texprofdir/man/texprofile.man new file mode 100644 index 00000000000..6fe0ce616e3 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/man/texprofile.man @@ -0,0 +1,596 @@ +'\" t +.\" Manual page created with latex2man on Mon Sep 9 16:27:26 2024 +.\" NOTE: This file is generated, DO NOT EDIT. +.de Vb +.ft CW +.nf +.. +.de Ve +.ft R + +.fi +.. +.TH "TEXPROFILE" "1" "6\-9\-2024" "Displaying TeX profiles " "Displaying TeX profiles " +.SH NAME + +.PP +texprofile +\- Displaying the profile data collected by texprof\&. +.PP +.SH SYNOPSIS + +.PP +texprofile +[Options] \fIinputfile\fP +.PP +.SH DESCRIPTION + +texprofile +reads the binary \fIinputfile\fP +as produced by +texprof\&. +The extension \&.tprof +of the input file can be +omitted. It analyses the data and presents the results in a variety of +tables, either optimized for human readability or as CSV (comma +separated values) files for further processing. +.PP +The output is written to the standard output stream where it can be redirected +to a file or viewed immediately. +.PP +.SH OPTIONS + +There are four types of options: general options, table options, +selection options, and formatting options. +.PP +The general options: +.TP +\fB\-?\fP \fB\-h\fP \fB\-\-help\fP + Display a short help text and exit the program. +.TP +\fB\-\-version\fP + Display the version information and exit. +.PP +The table options determine the tables that will be displayed. +These options all use upper case letters. +If no table option is given, only some global information is shown. +.PP +.TP +\fB\-T\fP + Show the table of the top 10 input lines. +.TP +\fB\-G\fP + Show the table of the macro call graph. +.TP +\fB\-C\fP + Show the table of times per TeX command. +.TP +\fB\-L\fP + Show the table of times per input line. +.TP +\fB\-R\fP + Show the table of raw time stamps. +.TP +\fB\-S\fP + Show the table of macro stack changes. +.TP +\fB\-F\fP + Show the table of all input files. +.TP +\fB\-M\fP + Show the table of all macros called. +.TP +\fB\-A\fP + Show important tables (equal to \-TGFC) tables. +.TP +\fB\-N\fP + Do not show the table of global information. +.PP +The selection options allow to reduce the amount of data that is shown +in the table by omitting table entries that contribute little to the +overall runtime. +.PP +.TP +\fB\-p\fP\fIn\fP + Do not show information for table entries +with cumulative time below \fIn\fP +percent. The default is 1.0 percent. +.TP +\fB\-t\fP\fIn\fP + Limit the number of input lines shown in the +table of the ``Top Ten\&'' to \fIn\fP\&. +This option is only useful +with the \fB\-T\fP +option. +.PP +The formatting option determine the formatting of the information in the tables +as well as the selection of information that is shown in the tables. +.PP +.TP +\fB\-i\fP + Add the macros file and line numbers after the macro name. +This option is useful to distinguish two macros that share +the same name. +.TP +\fB\-m\fP + Optimize output for machine readability. When optimized for human readability, +times are rounded and given in a short form adding a unit identifier: +s for seconds, ms for milliseconds, us for micro seconds, and ns for +nanoseconds. This format is inconvenient if you want to import the data +for example into a spread\-sheet for further processing. +With the \fB\-m\fP +option times are simply given in nano seconds without units. +Similar the combined time for the total time used for a macro and the +time used as a sub\-macro from the call graph is given in a single column +with a slash separating both numbers is nice for human readers. +With the \fB\-m\fP +option both numbers will simply be given in separate columns. +.PP +.TP +\fB\-n\fP + Show the time stamp numbers. This option is useful together with +the \fB\-R\fP +option, if you import the table into a database or spread\-sheet. +If you sort the data by various properties into different +categories, the time stamp number can be used to keeping the entries +within a category sorted by time. +.PP +.TP +\fB\-s\fP + Show the changes of the macro stack. +This option is useful with the \fB\-R\fP +option if you want to see the changes +in the macro stack (see \fB\-S\fP) +together with the timing information in +a single table. +.PP +.SH DATA FORMATS + +.SS FILE NUMBERS +While texprof +is running, every TeX input file is given a +unique file number. The file name alone is often not unique because +two files in different directories might have the same file +name. Displaying the full file name with the complete path is however often +not very +convenient. So if you observe the same file name together with +different file numbers, you can use the option \fB\-F\fP +to get a table of all +file numbers and their full file names. +.PP +But even within the same +directory, TeX can read different files with the same name during one +run: TeX can open a file for writing, write content to the file, close +it, open it for reading, read it and close it again. Then TeX might +repeat this process a second time, or multiple times, reusing the same +file name over and over again. texprof +will assign a new file +number to this file each time it is opened for reading. So you can +tell from the sequence of file numbers which one was the first, the +second, or the third. +.PP +Last not least, there are some special file +numbers: +.TP +0 \- unknown +If the file is unknown, which should rarely happen, +the file number 0 is used. +.TP +1\- system +texprof +will map +time intervals that are used to execute certain system routines +to the ``system\&'' pseudo file +using the line numbers to identify the specific routine +like producing the output DVI file (shipout), breaking +a paragraph into lines (linebrk), or breaking the +document into pages (buildpg). These times do not depend on the +use of macros but simply on the size of the document. +.TP +2 \- terminal +TeX commands entered on the command line or interactively are mapped +to file number 2. +.PP +.SS TIME +If the option \fB\-m\fP +is given times are given as nanoseconds. +Otherwise, times are rounded to at most 4 digits precission and +displayed with a unit: seconds (s), milliseconds (ms), microseconds +(um), or nanoseconds (ns). +.PP +.SS MACRO NAMES +Macro names are shown with the leading backslash. Since macro names +are often not unique, the command line option \fB\-i\fP +can be used to +show after the macro name in square brackets the file number and the +line number where the macro is defined. Unless two macros with the +same name are defined in the same file and line this is sufficient to +uniquely identify a macro. A macro defined with ``let\&'' results in a +complet copy of the original macro. Therefore it will not reference +the file and line of the ``let\&'' command but the same file and line as +the original macro. +.PP +.SH EXAMPLES + +Let\&'s assume that you issue the command texprof +\fB\-prof\fP +\fIhello.tex\fP\&. +This will run texprof +on the input file +\fIhello.tex\fP +with option \fB\-prof\fP\&. +The program texprof +will load the plain TeX format and then process hello.tex +to +produce hello.log +and hello.dvi\&. +It will execute +exactly the same steps that TeX would execute if you had issued the +command tex +\fIhello.tex\fP\&. +.PP +In addition to hello.log +and hello.dvi, +texprof +will also produce the file hello.tprof +containing time +measurements made while texprof +was running. The option +\fB\-prof\fP +will switch on the gathering of timing data as soon as +texprof +enters its main control procedure; The file +hello.tprof +will contain a time measurement, called a time +stamp, for every command that TeX executed while processing the input. +A binary format is used to store all that data in a simple and compact +form. Still the file hello.tprof +might become very big. +.PP +The program texprofile +is used to extract and analyse the data +contained in hello.tprof +and produce useful output. Here are +some examples: +.PP +.TP +texprofile \fIhello\fP + Without further options texprofile +will write some general +information to the standard output, like the total time measured, +the number of samples, the average time per sample, etc. +The general information is always given unless explicitely disabled +with the \fB\-N\fP +option or with the \fB\-m\fP +option. +.PP +.TP +texprofile \fB\-T\fP \fIhello\fP + With the option \fB\-T\fP, +texprofile +will map each time stamp +to a specific line of input, add up the time intervalls for each input +line separately, and output a table showing the ten lines that have +the highest cummulative time. +The table has the following seven columns: +.RS +.TP +1. file +The first column contains the input file number as explained above. +The input file name if shown in column 7. +.TP +2. line +The line number. +.TP +3. percent +The time spent in this line and file as a percentage of the total +time measured as given as part of the general information. +.TP +4. absolute +The absolute time spent in this line and file. +.TP +5. count +The number of times the execution path entered this line. +Note, that a +macro call usually redirects the execution path to another line +from where the execution will return after the macro call has completed. +Reentering the line after such an excursion to an other line will +cause this counter to be incremented. In summary, this counter might +reflect the number of partial executions of a line not the number +of full executions of the entire line. +.TP +6. average +The average time spent in the line is simply computed by dividing the +value in column 4 by the value in column 5. +.TP +7. file +The input file name. The corresponding file number is shown in column 1. +.RE +.RS +.PP +.RE +.TP +texprofile \fB\-G\fP \fIhello\fP + With the option \fB\-G\fP, +texprofile +will map each time stamp +either to file input or to a macro body. +The table shown is divided into several sections, the first section +is devoted to file input, each of the following sections is devoted +to a specific macro. +.br +Each section starts with a header. The header of the +first section is ``File\&'' the header of a later section is the macro name. +.br +The first line after the header gives the total time spent in the section +in two different formats: +.RS +.TP +1. time +Column 1 gives it as an absolut time. +.TP +3. percentage +Column 3 gives it as a percentage of the total time measured. +.RE +.RS +.PP +For the first section with the header ``File\&'', the absolute time +will be equal to the total time measured because texprof +did spent all the time processing the file \fIhello\fP\&. +And consequently the value in column 3 will be 100%\&. +.br +To accomplish a task, a macro usually calls other +macros that we call child macros in the following. +The following lines in the table will give a breakdown +of the time shown in the first line. +The breakdown starts with a line showing the time spent in the section +excluding the time spent in child macros. It shows in column 4 the number +of times the macro was called. +.RS +.RE +.TP +1. time +Column 1 gives the absolut time spent in the section excluding the time +spent in child macros +.TP +3. percentage +Column 3 gives the time from column 1 as a percentage of the total time +spent in this section as given in the previous line. +.TP +4. count/total +Column 4 gives the number of times the section was called. +.RE +.RS +.PP +The lines that follow in the table show the time spent in one of +the child macros. Column 2, 4, and 5 need some explanation. +.RS +.RE +.TP +2. loop +The only case where column 2 is not empty is the case of a +recursive macro. A recursive macro is a macro that along the +chain of macro calls eventually calls itself creating a +recursive loop. At this point, a macro becomesits own descendant +and at the same time its own ancestor. +.br +Therefore texprofile +maintains for each child macro two +accumulators for the elapsed time: For the time shown in +column 2 labeled ``loop\&'', texprofile +adds up the time +differences observed at the return of a child macro. For the +time shown in the column labeled ``time\&'' and ``percent\&'', it +subtracts from the time differences observed at the return of a +child macro all those time differences that were already added +to one of the other lines in the time breakdown: the macro itself +or one of the other child macros. +So the times shown in column 1 of line 2 and the following lines +will add up to the time shown in column 1 of the line 1; +and the percentages shown in column 3 of line 2 and the following lines +will add up to 100%\&. +The time shown in column 3 will show show the total time needed +to accomplish the sub task assigned to the respective child macro. +.TP +4. count/total +Column 4 shows two counts n/m for the macro named in column 5. +m is the total number of calls to the macro and n is the number of +calls as a child macro in the current section. The number n will +always be less or equal to m\&. +.RS +.PP +.RE +.TP +5. child +Column 5 shows the name of the child macro as explained above. +.RE +.RS +.PP +.RE +.TP +texprofile \fB\-G\fP \fB\-m\fP \fIhello\fP + This table will contain the same data as the previous example +but this time the \fB\-m\fP +option will optimize the output for +machine readability. +.RS +.TP +.B * +There are no column headers. +.TP +.B * +The times in column 1 ``time\&'' and column 2 ``loop\&'' +are given in nanoseconds without a unit identifier. +.TP +.B * +The numbers n/m in column 4 are now shown in two separate +columns 4 and 5; the macro name moves from column 5 to column 6. +.RE +.RS +.PP +.RE +.TP +texprofile \fB\-F\fP \fIhello\fP + With the option \fB\-F\fP, +texprofile +outputs the table +of all of TeX\&'s input files using 5 columns: +.RS +.TP +1. file +The file number as explained above. +.TP +2. lines +The difference between the highest and the lowest +line number found in the profile data for this file. This is +usually only a subset of all the lines of the file. +.TP +3. percent +The percentage of the total time measured that is attibuted to the file. +.TP +4. time +The absolute time that is attibuted to the file. +.TP +5. name +The full file name of the file. +.RE +.RS +.PP +.RE +.TP +texprofile \fB\-C\fP \fIhello\fP + With the option \fB\-C\fP, +texprofile +outputs the table +of all TeX commands executed while profiling +using 6 columns: +.RS +.TP +1. cmd +The command code used internaly by TeX. +Usually the same number is used by TeX for several +closely related tasks. +There is a special command code 101 which is used to +for the time that is spend on system routines that are +mapped to the system file as explained above. +.TP +2. time +The total time used for the command. +.TP +3. percent +The percentage of the total time measured used for the command. +.TP +4. count +The number of times this command was executed. +.TP +5. average +The average time needed to execute the command. +This is simpy the value in column 2 divided by the value in column 4. +.TP +6. name +A verbal description of the command or commands that share this +command code. +.RE +.RS +.PP +.RE +.TP +texprofile \fB\-R\fP \fB\-m\fP \fB\-n\fP \fIhello\fP + With the options \fB\-R\fP +\fB\-m\fP, +texprofile +outputs the table +of raw time measurements as observed by texprof +optimized for machine +readability. The option \fB\-n\fP +adds a column for the number +of each time measurements. +Because of the \fB\-m\fP +option, the table has no column headers. +The table has 7 columns: +.RS +.TP +1. number +The number of the time measurment. +These numbers are strictly increasing but not necessarily consecutive. +They can be used to keep the measurements sorted in the order in which +they were made. +.TP +2. file +The file number as explained above. +.TP +3. line +The line number as explained above. +.TP +4. time +The time interval in nano seconds. +.TP +5. command +The command name as given in column 6 of the previous example. +.TP +6. level +The nesting level of the macro call stack. +.TP +7. macro +The macro name. +.RE +.RS +.PP +This table contains all the timing information gathered during the run +of texprof\&. +Some information about macro calls is contained in +column 6. More information about macro calls could be added +by using the \fB\-s\fP +option. The information about macro returns +can be obtained from column 6. +The table can be imported to a spread\-sheet or a database for further +analysis. +.RE +.PP +.SH BUGS + +If the last command in a macro body is a macro call, we call this a tail call. +If such a tail call reads ahead to scan the following input +for possible arguments, the look\-ahead mechaism of TeX might push further +macros or new input files on TeX\&'s input stack. +These entries will remain on top of TeXprof\&'s macro nesting stack, +even if TeX backs up all these tokens on its input stack. +This can cause an attribution of runtime to those entries as sub entries +of the tail call. If this explanation sounds complicated to you, it is +because the situation is indeed complicated. +.PP +.SH SEE ALSO + +\fItexprof\fP(1) +.PP +.SH VERSION + +Version: 1.0 of 6-9-2024 +.TP +Copyright +(C)2024, Martin Ruckert, +.br +Hochschule München, Lothstrasse 64, D\-80335 München +.PP +.TP +Distribution +texprofile +is distributed with TeX Live. +More recent versions might be found on GitHub +\fBhttps://github.com/ruckertm/HINT\fP\&. +.PP +.TP +License +This program can be redistributed and/or modified under the +terms of the MIT/X11 license. +.PP +.SH AUTHOR + +Martin Ruckert +.br +Munich University of Applied Sciences +.br +Email: \fBmartin.ruckert@hm.edu\fP +.br +.PP +.\" NOTE: This file is generated, DO NOT EDIT. diff --git a/Build/source/texk/web2c/texprofdir/tex/latexprof.ini b/Build/source/texk/web2c/texprofdir/tex/latexprof.ini new file mode 100644 index 00000000000..889e6bec4ae --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/tex/latexprof.ini @@ -0,0 +1 @@ +\input latex.ltx diff --git a/Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini b/Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini new file mode 100644 index 00000000000..0460d192e29 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/tex/pdflatexprof.ini @@ -0,0 +1,2 @@ +\pdfoutput=1 +\input latex.ltx diff --git a/Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini b/Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini new file mode 100644 index 00000000000..8ee45c92265 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/tex/pdftexprof.ini @@ -0,0 +1,3 @@ +\pdfoutput=1 +\input plain.tex +\dump diff --git a/Build/source/texk/web2c/texprofdir/tex/texprof.ini b/Build/source/texk/web2c/texprofdir/tex/texprof.ini new file mode 100644 index 00000000000..74e0658f5b7 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/tex/texprof.ini @@ -0,0 +1,2 @@ +\input plain.tex +\dump diff --git a/Build/source/texk/web2c/texprofdir/texprof.w b/Build/source/texk/web2c/texprofdir/texprof.w new file mode 100644 index 00000000000..722106017fa --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/texprof.w @@ -0,0 +1,33304 @@ +% TeXprof is copyright (C) 2024 by Martin Ruckert +% Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen +% and put under the MIT/X11 license. +% +% Prote is copyright (C) 2021 by Thierry Laronde and put under +% the MIT/X11 license. +% +% As TeX and e-TeX are reserved names for the unchanged (except for the +% necessary implementation of system dependencies) instances of, resp., +% Donald E. Knuth's program and the NTS team's e-TeX implementation, the +% name Prote is reserved for this kerTeX implementation. +% +% Hence, this is neither TeX (even if it is compatible at start) nor +% e-TeX (even if is compatible when entering the first set of +% extensions). + +% Version 0 was released in September 1982 after it passed a variety of tests. +% Version 1 was released in November 1983 after thorough testing. +% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). +% Version 1.2 allowed `0' in response to an error, et alia (October 1984). +% Version 1.3 made memory allocation more flexible and local (November 1984). +% Version 1.4 fixed accents right after line breaks, et alia (April 1985). +% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). +% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). +% Version 2.1 corrected anomalies in discretionary breaks (January 1987). +% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). +% Version 2.3 avoided incomplete page in premature termination (August 1987). +% Version 2.4 fixed \noaligned rules in indented displays (August 1987). +% Version 2.5 saved cur_order when expanding tokens (September 1987). +% Version 2.6 added 10sp slop when shipping leaders (November 1987). +% Version 2.7 improved rounding of negative-width characters (November 1987). +% Version 2.8 fixed weird bug if no \patterns are used (December 1987). +% Version 2.9 made \csname\endcsname's "relax" local (December 1987). +% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). +% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). +% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). +% Version 2.94 kept open_log_file from calling fatal_error (November 1988). +% Version 2.95 solved that problem a better way (December 1988). +% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). +% Version 2.97 corrected blunder in creating 2.95 (February 1989). +% Version 2.98 omitted save_for_after at outer level (March 1989). +% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). +% Version 2.991 caught .5\ifdim.6... (June 1989). +% Version 2.992 introduced major changes for 8-bit extensions (September 1989). +% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). +% Version 3.0 fixed unusual displays; was more \output robust (March 1990). +% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). +% Version 3.14 fixed unprintable font names and corrected typos (March 1991). +% Version 3.141 more of same; reconstituted ligatures better (March 1992). +% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). +% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). +% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). +% Version 3.1415926 was a general cleanup with minor fixes (February 2008). +% Version 3.14159265 was similar (January 2014). +% Version 3.141592653 was similar but more extensive (January 2021). + +% A reward of $327.68 will be paid to the first finder of any remaining bug. + +% This is the 1.1 version of Prote, developed during August 2021, +% and corrected during september/october 2021 and amended in august 2023 +% for file primitives behavior matching input behavior. +% +% 1.0: adds primitives needed by LaTeX as listed in ltnews31. +% - 2022-07-21: tiddying formal fix: a spurious line was a left over +% of a removed paragraph (pointed by Martin Ruckert). Suppressed. +% +% 1.1: 2023-08-01: the new file primitives are used in LaTeX expecting +% the input behavior that ".tex" be appended if no extension. So +% modified to provide this. +% +% History towards 1.0 release: +% 0.99.4: +% - typos and style corrections provided by Martin Ruckert for +% clean translation in Pascal (forward declarations and underscores +% omitted); +% - adjustements against e-TeX 2.6 instead of 2.1 (T. Laronde). +% 0.99.7: +% - \expanded was using an e-TeX procedure not doing def handling +% => using scan_toks() now. Reported by Martin Ruckert and +% test code narrowed down by Phelype Oleinik. +% 0.99.8: +% - \expanded again: scan_toks() sets def_ref. But back_list() +% has to take not the refernce count, but the first token. +% Passing def_ref, a next invocation of scan_toks() was inserting +% a missing left brace and then reading pass the end of the token +% list to find a matching right one. def_ref => link(def_ref). +% Test code narrowed down by Phelype Oleinik (a great help!). +% - The API for file related primitives has been changed: no error +% is reported on failure to find/open and nothing is returned +% (matches current other implementations). +% 0.99.9: +% - Fix typos in Pascal replacement text; xchg_buffer_length=0 => +% xchg_buffer_length:=0 (caught by Martin Ruckert). +% 0.99.10: +% - KerTeX Public License -> X11/MIT license. +% 1.0: +% - Just naming the official release. No change. +% +% This work was done by Thierry Laronde and is under the MIT/X11 +% license. +% +% + +% Here is TeX material that gets inserted after \input webmac +\def\hang{\hangindent 3em\noindent\ignorespaces} +\def\hangg#1 {\hang\hbox{#1 }} +\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces} +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like SAIL +\def\Prote{{\tenrm P\kern-0.1em R\kern-0.15em\raise.11ex\hbox{o}% + \kern-0.22em T\kern-0.05em E}} +\def\texprof{{\TeX}prof} +\ifpdftex +\sanitizecommand{\texprof}{TeXprof} +\sanitizecommand{\eTeX}{eTeX} +\sanitizecommand{\Prote}{PRoTE} +\fi +\font\tenlogo=logo10 +\def\MP{{\tenlogo META}\-{\tenlogo POST}} +\def\eTeX{$\varepsilon$-\TeX} +\font\revrm=xbmc10 % for right-to-left text +% to generate xbmc10 (i.e., reflected cmbx10) use a file +% xbmc10.mf containing: +%+++++++++++++++++++++++++++++++++++++++++++++++++ +% if unknown cmbase: input cmbase fi +% extra_endchar := extra_endchar & +% "currentpicture:=currentpicture " & +% "reflectedabout((.5[l,r],0),(.5[l,r],1));"; +% input cmbx10 +%+++++++++++++++++++++++++++++++++++++++++++++++++ +\ifx\beginL\undefined % this is TeX + \def\XeT{X\kern-.125em\lower.5ex\hbox{E}\kern-.1667emT} + \def\TeXeT{\TeX-\hbox{\revrm \XeT}} % for TeX-XeT + \def\TeXXeT{\TeX-\hbox{\revrm -\XeT}} % for TeX--XeT +\else + \ifx\eTeXversion\undefined % this is \TeXeT + \def\TeXeT{\TeX-{\revrm\beginR\TeX\endR}} % for TeX-XeT + \def\TeXXeT{\TeX-{\revrm\beginR\TeX-\endR}} % for TeX--XeT + \else % this is \eTeX + \def\TeXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX\endR}} % for TeX-XeT + \def\TeXXeT{\TeX-{\TeXXeTstate=1\revrm\beginR\TeX-\endR}} % for TeX--XeT + \fi +\fi +\def\PASCAL{Pascal} +\def\pdfTeX{pdf\TeX} +\def\ph{\hbox{Pascal-H}} +\def\pct!{{\char`\%}} % percent sign in ordinary text +\def\grp{\.{\char'173...\char'175}} +\font\logo=logo10 % font used for the METAFONT logo +\def\MF{{\logo META}\-{\logo FONT}} +\def\<#1>{$\langle#1\rangle$} +\def\section{\mathhexbox278} + +\def\(#1){} % this is used to make section names sort themselves better +\def\9#1{} % this is used for sort keys in the index via @@:sort key}{entry@@> + +\let\@@=\relax % we want to be able to \write a \? + +\def\title{\texprof} +\def\LaTeX{L\kern-.36em\raise.3ex\hbox{\sc A}\kern-.15em\TeX}% +% system dependent redefinitions of \title should come later +% and should use: +% \toks0=\expandafter{\title} +% \edef\title{...\the\toks0...} +%\let\maybe=\iffalse % print only changed modules +\def\topofcontents{\hsize 5.5in + \vglue 0pt plus 1fil minus 1.5in + \def\@@##1]{\hbox to 1in{\hfil##1.\ }} + } +\def\botofcontents{\vskip 0pt plus 1fil minus 1.5in} +\pageno=3 +\def\glob{13} % this should be the section number of "<Global...>" +\def\gglob{20, 26} % this should be the next two sections of "<Global...>" + +\def\.#1{\leavevmode\hbox{\tentex % typewriter type for strings + \let\\=\BS % backslash in a string + \let\'=\RQ % right quote in a string + \let\`=\LQ % left quote in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + #1\kern.05em}} +\def\{\leavevmode\hbox{\bf\def\_{\UL}% + #1\/\kern.05em}} % boldface type for reserved words +\def\\#1{\leavevmode\hbox{\it\def\_{\UL}% + #1\/\kern.05em}} % italic type for identifiers +\def\vb#1{{\rm #1}} +%\def\^{\ifmmode\mathchar"222 \else\char`^ \fi} % pointer or hat +\def\LQ{{\tt\char'22}} % left quote in a string +\def\RQ{{\tt\char'23}} % right quote in a string +\def\UL{{\tt\char`\_}} % underline character in a C identifier +\def\CF{{\tt\char`^}} +\def\dotdot{\mathrel{.\,.}} % double dot, used only in math mode +\setbox\MGbox=\hbox{\kern1pt$\rightarrow$\kern1pt} % C pointer to field +\def\MG{\copy\MGbox} +@s dotdot TeX +@s alpha_file int +@s byte_file int +@s word_file int +@s uint8_t int +@s int16_t int +@s uint16_t int +@s int32_t int +@s uint32_t int +@s halfword int +@s nonnegative_integer int +@s small_number int +@s glue_ratio double +@s Xdimen int +@s Kern int +@s Font int +@s Glue int +@s Disc int +@s Lig int +@s Rule int +@s List int +@s Kind int +@s Info int +@s kpse_file_format_type int +@s Stretch int +@s in TeX +@s line normal +@s to do + +@* Introduction. +This is \texprof, an implementation of \TeX\ with an extension providing profiling +capabilities. To be useful nowadays, the profiling capability is not the only +extension of Donald Knuth's original implementation of \TeX. + +To obtain the present \CEE/ program, Donald Knuth's \PASCAL\ program was +modified in a first step using most of the \eTeX\ extensions by +Peter Breitenlohner and Phil Taylor. +@^Breitenlohner, Peter@> +@^Taylor, Phil@> +Then further extensions were added using the \Prote\ change file +of Thierry Laronde. +@^Laronde, Thierry@> +Then the constants defining the size of \TeX's data structures, +notably the size of the |mem| array, were increased considerable. All these +changes to the \PASCAL\ program are necessary to run a current \LaTeX\ on the +resulting \TeX\ engine. The next important step was then to translate this +literate \PASCAL\ program to a literate \CEE/ program using the +{\tt web2w} source code translator. The use of the \CEE/ based \.{cweb} system +instead of the \PASCAL\ based \.{WEB} system greatly simplifies the tool chain +necessary for producing an executable program and allows true source level +debugging of the resulting program. The latter is indispensable to carry out +large scale modifications of the \TeX\ program and is of enormous help even +for smaller modifications. +The first modification of the resulting \CEE/ program was then the addition +of \CEE/ routines from the \TeX\ Live project to enable the use of the +kpath search library by Karl Berry. +@^Berry, Karl@> +The last and final step was the addition of profiling capabilities +which includes, as the latest addition, the definition of numerous +primitives that enable the \TeX\ profiler to pretend to be \pdfTeX. +The code used there reuses parts of the \CEE/ version of \pdfTeX\ by +H\`an Th\^e\llap{\raise 0.5ex\hbox{\'{}}} Th\`anh. +@^Th\`anh, H\`an Th\^e\llap{\raise 0.5ex\hbox{\'{}}}@> + +All these changes left their traces in the text that follows. +No attempt was made to streamline the text and rid it from the traces +of its exciting history. +So with the next paragraph you can start reading Donald Knuth's +introduction to his \TeX\ program. Enjoy! +\medskip + +This is \TeX, a document compiler intended to produce typesetting of high +quality. +The \PASCAL\ program that follows is the definition of \TeX82, a standard +@:PASCAL}{\PASCAL@> +@!@:TeX82}{\TeX82@> +version of \TeX\ that is designed to be highly portable so that identical output +will be obtainable on a great variety of computers. + +The main purpose of the following program is to explain the algorithms of \TeX\ +as clearly as possible. As a result, the program will not necessarily be very +efficient when a particular \PASCAL\ compiler has translated it into a +particular machine language. However, the program has been written so that it +can be tuned to run efficiently in a wide variety of operating environments +by making comparatively few changes. Such flexibility is possible because +the documentation that follows is written in the \.{WEB} language, which is +at a higher level than \PASCAL; the preprocessing step that converts \.{WEB} +to \PASCAL\ is able to introduce most of the necessary refinements. +Semi-automatic translation to other languages is also feasible, because the +program below does not make extensive use of features that are peculiar to +\PASCAL. + +A large piece of software like \TeX\ has inherent complexity that cannot +be reduced below a certain level of difficulty, although each individual +part is fairly simple by itself. The \.{WEB} language is intended to make +the algorithms as readable as possible, by reflecting the way the +individual program pieces fit together and by providing the +cross-references that connect different parts. Detailed comments about +what is going on, and about why things were done in certain ways, have +been liberally sprinkled throughout the program. These comments explain +features of the implementation, but they rarely attempt to explain the +\TeX\ language itself, since the reader is supposed to be familiar with +{\sl The \TeX book}. +@.WEB@> +@:TeXbook}{\sl The \TeX book@> + +@ The present implementation has a long ancestry, beginning in the summer +of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded +a prototype +@^Plass, Michael Frederick@> +@^Liang, Franklin Mark@> +@^Knuth, Donald Ervin@> +based on some specifications that the author (in the following, unless +specified, ``the author'' refers to D.E.~Knuth) had made in May of that year. +This original proto\TeX\ included macro definitions and elementary +manipulations on boxes and glue, but it did not have line-breaking, +page-breaking, mathematical formulas, alignment routines, error recovery, +or the present semantic nest; furthermore, +it used character lists instead of token lists, so that a control sequence +like \.{\\halign} was represented by a list of seven characters. A +complete version of \TeX\ was designed and coded by the author in late +1977 and early 1978; that program, like its prototype, was written in the +{\mc SAIL} language, for which an excellent debugging system was +available. Preliminary plans to convert the {\mc SAIL} code into a form +somewhat like the present ``web'' were developed by Luis Trabb~Pardo and +@^Trabb Pardo, Luis Isidoro@> +the author at the beginning of 1979, and a complete implementation was +created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which +@^Zabala Salelles, Ignacio Andr\'es@> +was written by the author during the latter part of 1981 and the early +part of 1982, also incorporates ideas from the 1979 implementation of +@^Guibas, Leonidas Ioannis@> +@^Sedgewick, Robert@> +@^Wyatt, Douglas Kirk@> +\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick, +and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred +refinements were introduced into \TeX82 based on the experiences gained with +the original implementations, so that essentially every part of the system +has been substantially improved. After the appearance of ``Version 0'' in +September 1982, this program benefited greatly from the comments of +many other people, notably David~R. Fuchs and Howard~W. Trickey. +A final revision in September 1989 extended the input character set to +eight-bit codes and introduced the ability to hyphenate words from +different languages, based on some ideas of Michael~J. Ferguson. +@^Fuchs, David Raymond@> +@^Trickey, Howard Wellington@> +@^Ferguson, Michael John@> + +No doubt there still is plenty of room for improvement, but the author +is firmly committed to keeping \TeX82 ``frozen'' from now on; stability +and reliability are to be its main virtues. + +On the other hand, the \.{WEB} description can be extended without changing +the core of \TeX82 itself, and the program has been designed so that such +extensions are not extremely difficult to make. +The |banner| string defined here should be changed whenever \TeX\ +undergoes any modifications, so that it will be clear which version of +\TeX\ might be the guilty party when a problem arises. +@^extensions to \TeX@> +@^system dependencies@> + +This program contains code for various features extending \TeX, +therefore this program is called `\Prote' and not +`\TeX'; the official name `\TeX' by itself is reserved +for software systems that are fully compatible with each other. +A special test suite called the ``\.{TRIP} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\TeX' [cf.~Stanford Computer Science report CS1027, +November 1984]. + +A similar test suite called the ``\.{SELLETTE} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\Prote'. + +@d eTeX_version 2 /* \.{\\eTeXversion} */ +@d eTeX_revision ".6" /* \.{\\eTeXrevision} */ +@d eTeX_version_string "-2.6" /*current \eTeX\ version*/ +@# +@d TeX_banner "This is TeX, Version 3.141592653" /*printed when \TeX\ starts*/ +@# +@# +@d TEX ETEX /*change program name into |ETEX|*/ +@# +@d eTeX_states 1 /*number of \eTeX\ state variables in |eqtb|*/ +@# +@d Prote_version_string "3.141592653-2.6-1.1.0" /*current \Prote\ version*/ +@d Prote_version 1 /* \.{\\Proteversion} */ +@d Prote_revision ".1.0" /* \.{\\Proterevision} */ +@# +@d Prote_banner "This is Prote, Version " Prote_version_string + /*printed when \Prote\ starts*/ +@# +@d banner Prote_banner + +@ Different \PASCAL s have slightly different conventions, and the present +@!@:PASCAL H}{\ph@> +program expresses \TeX\ in terms of the \PASCAL\ that was +available to the author in 1982. Constructions that apply to +this particular compiler, which we shall call \ph, should help the +reader see how to make an appropriate interface for other systems +if necessary. (\ph\ is Charles Hedrick's modification of a compiler +@^Hedrick, Charles Locke@> +for the DECsystem-10 that was originally developed at the University of +Hamburg; cf.\ {\sl Software---Practice and Experience \bf6} (1976), +29--42. The \TeX\ program below is intended to be adaptable, without +extensive changes, to most other versions of \PASCAL, so it does not fully +use the admirable features of \ph. Indeed, a conscious effort has been +made here to avoid using several idiosyncratic features of standard +\PASCAL\ itself, so that most of the code can be translated mechanically +into other high-level languages. For example, the `\&{with}' and `\\{new}' +features are not used, nor are pointer types, set types, or enumerated +scalar types; there are no `\&{var}' parameters, except in the case of files +--- \eTeX, however, does use `\&{var}' parameters for the |reverse| function; +there are no tag fields on variant records; there are no assignments +|double=int|; no procedures are declared local to other procedures.) + +The portions of this program that involve system-dependent code, where +changes might be necessary because of differences between \PASCAL\ compilers +and/or differences between +operating systems, can be identified by looking at the sections whose +numbers are listed under `system dependencies' in the index. Furthermore, +the index entries for `dirty \PASCAL' list all places where the restrictions +of \PASCAL\ have not been followed perfectly, for one reason or another. +@!@^system dependencies@> +@!@^dirty \PASCAL@> + +Incidentally, \PASCAL's standard |round| function can be problematical, +because it disagrees with the IEEE floating-point standard. +Many implementors have +therefore chosen to substitute their own home-grown rounding procedure. + +@ The following is an outline of the program, whose +components will be filled in later, using the conventions of \.{cweb}. +@.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. + +The program starts with inserting header files and occassionaly a function +must be placed before declaring \TeX's macros, because the function +uses identifiers that \TeX will declare as macros. + +@p @<Header files and function declarations@>@; +@h +enum {@<Constants in the outer block@>@; + @!empty_string=256 /*the empty string follows after 256 characters*/ +}; +@<Types in the outer block@>@; +@<Forward declarations@>@; +@<Global variables@>@; +@# +static void initialize(void) /*this procedure gets things started properly*/ + {@+@<Local variables for initialization@>@; + @<Initialize whatever \TeX\ might access@>; + } @# +@<Basic printing procedures@>@; +@<Error handling procedures@>@; + +@ The overall \TeX\ program begins with the heading just shown, after which +comes a bunch of procedure declarations and function declarations. +Finally we will get to the main program, which begins with the +comment `|start_here|'. If you want to skip down to the +main program now, you can look up `|start_here|' in the index. +But the author suggests that the best way to understand this program +is to follow pretty much the order of \TeX's components as they appear in the +\.{WEB} description you are now reading, since the present ordering is +intended to combine the advantages of the ``bottom up'' and ``top down'' +approaches to the problem of understanding a somewhat complicated system. + +@ There is no need to declare labels in \CEE/. + +@ Some of the code below is intended to be used only when diagnosing the +strange behavior that sometimes occurs when \TeX\ is being installed or +when system wizards are fooling around with \TeX\ without quite knowing +what they are doing. Such code will not normally be compiled; it is +delimited by the codewords `$|@t\#\&{ifdef} \.{DEBUG}@>|\ldots|@t\#\&{endif}@>|$', with apologies +to people who wish to preserve the purity of English. + +Similarly, there is some conditional code delimited by +`$|@t\#\&{ifdef} \.{STAT}@>|\ldots|@t\#\&{endif}@>|$' that is intended for use when statistics are to be +kept about \TeX's memory usage. The |@t\#\&{ifdef} \.{STAT}@>| $\ldots$ |@t\#\&{endif}@>| code also +implements diagnostic information for \.{\\tracingparagraphs}, +\.{\\tracingpages}, and \.{\\tracingrestores}. +@^debugging@> + +@ This program has two important variations: (1) There is a long and slow +version called \.{INITEX}, which does the extra calculations needed to +@.INITEX@> +initialize \TeX's internal tables; and (2)~there is a shorter and faster +production version, which cuts the initialization to a bare minimum. +Parts of the program that are needed in (1) but not in (2) are delimited by +the codewords `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$'. + +\TeX\ Live has established the common practice +to select the initialization code at runtime +using the |iniversion| variable. + +@<Initialize whatever...@>= +@<Set initial values of key variables@>@/ +#ifdef @!INIT +if (iniversion) /* \TeX\ Live*/ + {@+@<Initialize table entries (done by \.{INITEX} only)@>@;@+} +#endif + +@ The declaration of the necessary type definitions are +contained in header files. + + +@<Header files and function declarations@>= +#include <stdbool.h> +#include <string.h> +#include <math.h> + +@ Further it is necessary to define some build in primitives of +\PASCAL\ that are otherwise not available in~\CEE/. +@:PASCAL H}{\ph@> + +@d odd(X) ((X)&1) +@d chr(X) ((unsigned char)(X)) +@d ord(X) ((unsigned int)(X)) +@d abs(X) ((X)>-(X)?(X):-(X)) +@d round(X) ((int)((X)>=0.0?floor((X)+0.5):ceil((X)-0.5))) + +@ The following parameters can be changed at compile time to extend or +reduce \TeX's capacity. They may have different values in \.{INITEX} and +in production versions of \TeX. +@.INITEX@> +@^system dependencies@> + +@<Constants...@>= +@!mem_max=5000000, /*greatest index in \TeX's internal |mem| array; + must be strictly less than |max_halfword|; + must be equal to |mem_top| in \.{INITEX}, otherwise | >= mem_top|*/ +@!mem_min=0, /*smallest index in \TeX's internal |mem| array; + must be |min_halfword| or more; + must be equal to |mem_bot| in \.{INITEX}, otherwise | <= mem_bot|*/ +@!buf_size=2000000, /*maximum number of characters simultaneously present in + current lines of open files and in control sequences between + \.{\\csname} and \.{\\endcsname}; must not exceed |max_halfword|*/ +@!error_line=79, /*width of context lines on terminal error messages*/ +@!half_error_line=50, /*width of first lines of contexts in terminal + error messages; should be between 30 and |error_line-15|*/ +@!max_print_line=79, /*width of longest text lines output; should be at least 60*/ +@!stack_size=5000, /*maximum number of simultaneous input sources*/ +@!max_in_open=15, /*maximum number of input files and error insertions that + can be going on simultaneously*/ +@!font_max=255, /*maximum internal font number; must not exceed |max_quarterword| + and must be at most |font_base+256|*/ +@!font_mem_size=8000000, /*number of words of |font_info| for all fonts*/ +@!param_size=10000, /*maximum number of simultaneous macro parameters*/ +@!nest_size=500, /*maximum number of semantic levels simultaneously active*/ +@!max_strings=500000, /*maximum number of strings; must not exceed |max_halfword|*/ +@!string_vacancies=90000, /*the minimum number of characters that should be + available for the user's control sequences and font names, + after \TeX's own error messages are stored*/ +@!pool_size=6250000, /*maximum number of characters in strings, including all + error messages and help texts, and the names of all fonts and + control sequences; must exceed |string_vacancies| by the total + length of \TeX's own strings, which is currently about 23000*/ +@!save_size=100000, /*space for saving values outside of current group; must be + at most |max_halfword|*/ +@!trie_size=1000000, /*space for hyphenation patterns; should be larger for + \.{INITEX} than it is in production versions of \TeX*/ +@!trie_op_size=35111, /*space for ``opcodes'' in the hyphenation patterns*/ +@!dvi_buf_size=16384, /*size of the output buffer; must be a multiple of 8*/ +@!file_name_size=1024, /*file names shouldn't be longer than this*/ +@!xchg_buffer_size=64, /*must be at least 64*/ + /*size of |eight_bits| buffer for exchange with system routines*/ + +@ Like the preceding parameters, the following quantities can be changed +at compile time to extend or reduce \TeX's capacity. But if they are changed, +it is necessary to rerun the initialization program \.{INITEX} +@.INITEX@> +to generate new tables for the production \TeX\ program. +One can't simply make helter-skelter changes to the following constants, +since certain rather complex initialization +numbers are computed from them. They are defined here using +\.{WEB} macros, instead of being put into the above |enum| list in order to +emphasize this distinction. + +@d mem_bot 0 /*smallest index in the |mem| array dumped by \.{INITEX}; + must not be less than |mem_min|*/ +@d mem_top 5000000 /*largest index in the |mem| array dumped by \.{INITEX}; + must be substantially larger than |mem_bot| + and not greater than |mem_max|*/ +@d font_base 0 /*smallest internal font number; must not be less + than |min_quarterword|*/ +@d hash_size 45000 /*maximum number of control sequences; it should be at most + about |(mem_max-mem_min)/(double)10|*/ +@d hash_prime 35999 /*a prime number equal to about 85\pct! of |hash_size|*/ +@d hyph_size 8191 /*another prime; the number of \.{\\hyphenation} exceptions*/ +@^system dependencies@> + +@ In case somebody has inadvertently made bad settings of the ``constants,'' +\TeX\ checks them using a global variable called |bad|. + +This is the first of many sections of \TeX\ where global variables are +defined. + +@<Glob...@>= +static int @!bad; /*is some ``constant'' wrong?*/ + +@ Later on we will say `\ignorespaces|if (mem_max >= max_halfword) bad=14|', +or something similar. (We can't do that until |max_halfword| has been defined.) + +@<Check the ``constant'' values for consistency@>= +bad=0; +if ((half_error_line < 30)||(half_error_line > error_line-15)) bad=1; +if (max_print_line < 60) bad=2; +if (dvi_buf_size%8!=0) bad=3; +if (mem_bot+1100 > mem_top) bad=4; +if (hash_prime > hash_size) bad=5; +if (max_in_open >= 128) bad=6; +if (mem_top < 256+11) bad=7; /*we will want |null_list > 255|*/ + +@ Labels are given symbolic names by the following definitions, so that +occasional |goto| statements will be meaningful. We insert the label +`|end|' just before the `\ignorespaces|} |\unskip' of a procedure in +which we have used the `|goto end|' 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 +`|resume|'. 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. + +@ Here are some macros for common programming idioms. + +@d incr(A) A=A+1 /*increase a variable by unity*/ +@d decr(A) A=A-1 /*decrease a variable by unity*/ +@d negate(A) A=-A /*change the sign of a variable*/ +@d loop @+while (true) @+ /*repeat over and over until a |goto| happens*/ +@f loop else + /*\.{WEB}'s |else| acts like `\ignorespaces|while true do|\unskip'*/ +@d do_nothing /*empty statement*/ +@d empty 0 /*symbolic name for a null constant*/ + +@* The character set. +In order to make \TeX\ readily portable to a wide variety of +computers, all of its input text is converted to an internal eight-bit +code that includes standard ASCII, the ``American Standard Code for +Information Interchange.'' This conversion is done immediately when each +character is read in. Conversely, characters are converted from ASCII to +the user's external representation just before they are output to a +text file. + +Such an internal code is relevant to users of \TeX\ primarily because it +governs the positions of characters in the fonts. For example, the +character `\.A' has ASCII code $65=0101$, and when \TeX\ typesets +this letter it specifies character number 65 in the current font. +If that font actually has `\.A' in a different position, \TeX\ doesn't +know what the real position is; the program that does the actual printing from +\TeX's device-independent files is responsible for converting from ASCII to +a particular font encoding. +@^ASCII code@> + +\TeX's internal code also defines the value of constants +that begin with a reverse apostrophe; and it provides an index to the +\.{\\catcode}, \.{\\mathcode}, \.{\\uccode}, \.{\\lccode}, and \.{\\delcode} +tables. + +@ Characters of text that have been converted to \TeX's internal form +are said to be of type |ASCII_code|, which is a subrange of the integers. + +@<Types...@>= +typedef uint8_t ASCII_code; /*eight-bit numbers*/ + +@ The original \PASCAL\ compiler was designed in the late 60s, when six-bit +character sets were common, so it did not make provision for lowercase +letters. Nowadays, of course, we need to deal with both capital and small +letters in a convenient way, especially in a program for typesetting; +so the present specification of \TeX\ has been written under the assumption +that the \PASCAL\ compiler and run-time system permit the use of text files +with more than 64 distinguishable characters. More precisely, we assume that +the character set contains at least the letters and symbols associated +with ASCII codes 040 through 0176; 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 |unsigned char| for the +characters in text files, even though there now are more than 64 such +characters, while other \PASCAL s consider |unsigned 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@> + +@s text_char char +@d text_char unsigned 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...@>= +int @!i; + +@ The \TeX\ processor converts between ASCII code and +the user's external character set by means of arrays |xord| and |xchr| +that are analogous to \PASCAL's |ord| and |chr| functions. + +@<Glob...@>= +static ASCII_code @!xord[256]; + /*specifies conversion of input characters*/ +static text_char @!xchr[256]; + /*specifies conversion of output characters*/ + +@ Since we are assuming that our \PASCAL\ system is able to read and +write the visible characters of standard ASCII (although not +necessarily using the ASCII codes to represent them), the following +assignment statements initialize the standard part of the |xchr| array +properly, without needing any system-dependent changes. On the other +hand, it is possible to implement \TeX\ with less complete character +sets, and in such cases it will be necessary to change something here. +@^system dependencies@> + +@<Set init...@>= +xchr[040]=' '; +xchr[041]='!'; +xchr[042]='"'; +xchr[043]='#'; +xchr[044]='$'; +xchr[045]='%'; +xchr[046]='&'; +xchr[047]='\'';@/ +xchr[050]='('; +xchr[051]=')'; +xchr[052]='*'; +xchr[053]='+'; +xchr[054]=','; +xchr[055]='-'; +xchr[056]='.'; +xchr[057]='/';@/ +xchr[060]='0'; +xchr[061]='1'; +xchr[062]='2'; +xchr[063]='3'; +xchr[064]='4'; +xchr[065]='5'; +xchr[066]='6'; +xchr[067]='7';@/ +xchr[070]='8'; +xchr[071]='9'; +xchr[072]=':'; +xchr[073]=';'; +xchr[074]='<'; +xchr[075]='='; +xchr[076]='>'; +xchr[077]='?';@/ +xchr[0100]='@@'; +xchr[0101]='A'; +xchr[0102]='B'; +xchr[0103]='C'; +xchr[0104]='D'; +xchr[0105]='E'; +xchr[0106]='F'; +xchr[0107]='G';@/ +xchr[0110]='H'; +xchr[0111]='I'; +xchr[0112]='J'; +xchr[0113]='K'; +xchr[0114]='L'; +xchr[0115]='M'; +xchr[0116]='N'; +xchr[0117]='O';@/ +xchr[0120]='P'; +xchr[0121]='Q'; +xchr[0122]='R'; +xchr[0123]='S'; +xchr[0124]='T'; +xchr[0125]='U'; +xchr[0126]='V'; +xchr[0127]='W';@/ +xchr[0130]='X'; +xchr[0131]='Y'; +xchr[0132]='Z'; +xchr[0133]='['; +xchr[0134]='\\'; +xchr[0135]=']'; +xchr[0136]='^'; +xchr[0137]='_';@/ +xchr[0140]='`'; +xchr[0141]='a'; +xchr[0142]='b'; +xchr[0143]='c'; +xchr[0144]='d'; +xchr[0145]='e'; +xchr[0146]='f'; +xchr[0147]='g';@/ +xchr[0150]='h'; +xchr[0151]='i'; +xchr[0152]='j'; +xchr[0153]='k'; +xchr[0154]='l'; +xchr[0155]='m'; +xchr[0156]='n'; +xchr[0157]='o';@/ +xchr[0160]='p'; +xchr[0161]='q'; +xchr[0162]='r'; +xchr[0163]='s'; +xchr[0164]='t'; +xchr[0165]='u'; +xchr[0166]='v'; +xchr[0167]='w';@/ +xchr[0170]='x'; +xchr[0171]='y'; +xchr[0172]='z'; +xchr[0173]='{'; +xchr[0174]='|'; +xchr[0175]='}'; +xchr[0176]='~';@/ + +@ Some of the ASCII codes without visible characters have been given symbolic +names in this program because they are used with a special meaning. + +@d null_code 00 /*ASCII code that might disappear*/ +@d carriage_return 015 /*ASCII code used at end of line*/ +@d invalid_code 0177 /*ASCII code that many systems prohibit in text files*/ + +@ The ASCII code is ``standard'' only to a certain extent, since many +computer installations have found it advantageous to have ready access +to more than 94 printing characters. Appendix~C of {\sl The \TeX book\/} +gives a complete specification of the intended correspondence between +characters and \TeX's internal representation. +@:TeXbook}{\sl The \TeX book@> + +If \TeX\ is being used +on a garden-variety \PASCAL\ for which only standard ASCII +codes will appear in the input and output files, it doesn't really matter +what codes are specified in |xchr[0 dotdot 037]|, but the safest policy is to +blank everything out by using the code shown below. + +However, other settings of |xchr| will make \TeX\ more friendly on +computers that have an extended character set, so that users can type things +like `\.^^Z' instead of `\.{\\ne}'. People with extended character sets can +assign codes arbitrarily, giving an |xchr| equivalent to whatever +characters the users of \TeX\ are allowed to have in their input files. +It is best to make the codes correspond to the intended interpretations as +shown in Appendix~C whenever possible; but this is not necessary. For +example, in countries with an alphabet of more than 26 letters, it is +usually best to map the additional letters into codes less than~040. +To get the most ``permissive'' character set, change |' '| on the +right of these assignment statements to |chr(i)|. +@^character set dependencies@> +@^system dependencies@> + +@<Set init...@>= +for (i=0; i<=037; i++) xchr[i]=chr(i); /* \TeX\ Live*/ +for (i=0177; i<=0377; i++) xchr[i]=chr(i); /* \TeX\ Live*/ + +@ 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 < 0177|, 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 040 in case there is a coincidence. + +@<Set init...@>= +for (i=first_text_char; i<=last_text_char; i++) xord[chr(i)]=invalid_code; +for (i=0200; i<=0377; i++) xord[xchr[i]]=i; +for (i=0; i<=0176; i++) xord[xchr[i]]=i; + +@* Input and output. +The bane of portability is the fact that different operating systems treat +input and output quite differently, perhaps because computer scientists +have not given sufficient attention to this problem. People have felt somehow +that input and output are not part of ``real'' programming. Well, it is true +that some kinds of programming are more fun than others. With existing +input/output conventions being so diverse and so messy, the only sources of +joy in such parts of the code are the rare occasions when one can find a +way to make the program a little less bad than it might have been. We have +two choices, either to attack I/O now and get it over with, or to postpone +I/O until near the end. Neither prospect is very attractive, so let's +get it over with. + +The basic operations we need to do are (1)~inputting and outputting of +text, to or from a file or the user's terminal; (2)~inputting and +outputting of eight-bit bytes, to or from a file; (3)~instructing the +operating system to initiate (``open'') or to terminate (``close'') input or +output from a specified file; (4)~testing whether the end of an input +file has been reached. + +\TeX\ needs to deal with two kinds of files. +We shall use the term |alpha_file| for a file that contains textual data, +and the term |byte_file| for a file that contains eight-bit binary information. +These two types turn out to be the same on many computers, but +sometimes there is a significant distinction, so we shall be careful to +distinguish between them. Standard protocols for transferring +such files from computer to computer, via high-speed networks, are +now becoming available to more and more communities of users. + +The program actually makes use also of a third kind of file, called a +|word_file|, when dumping and reloading base information for its own +initialization. We shall define a word file later; but it will be possible +for us to specify simple operations on word files before they are defined. + +@<Types...@>= +typedef uint8_t eight_bits; /*unsigned one-byte quantity*/ +typedef struct {@+FILE *f;@+text_char@,d;@+} alpha_file; /*files that contain textual data*/ +typedef struct {@+FILE *f;@+eight_bits@,d;@+} byte_file; /*files that contain binary data*/ + +@ Most of what we need to do with respect to input and output can be handled +by the I/O facilities that are standard in \PASCAL, i.e., the routines +called |get|, |put|, |eof|, and so on. But +standard \PASCAL\ does not allow file variables to be associated with file +names that are determined at run time, so it cannot be used to implement +\TeX; some sort of extension to \PASCAL's ordinary |reset| and |rewrite| +is crucial for our purposes. We shall assume that |name_of_file| is a variable +of an appropriate type such that the \PASCAL\ run-time system being used to +implement \TeX\ can open a file whose external name is specified by +|name_of_file|. +@^system dependencies@> + +@<Glob...@>= +static unsigned char @!name_of_file0[file_name_size+1]={0}, + *const @!name_of_file = @!name_of_file0-1;@;@/ + /*on some systems this may be a \&{record} variable*/ +static int @!name_length;@/ /*this many characters are actually + relevant in |name_of_file| (the rest are blank)*/ + +@ To open files, \TeX\ used \PASCAL's |reset| function. +We use the {\tt kpathsearch} library to implement new functions +in the section on \TeX\ Live Integration. +Here we give only the function prototypes. + +\TeX's file-opening functions do not issue their own +error messages if something goes wrong. If a file identified by +|name_of_file| 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) +\TeX's file-opening functions return |false|. +This allows \TeX\ to undertake appropriate corrective action. +@^system dependencies@> + +@p +static FILE*open_in(char*filename,kpse_file_format_type t,const char*rwb); /* \TeX\ Live*/ +static bool a_open_in(alpha_file *f); /*open a text file for input*/ +static bool b_open_in(byte_file *f); /*open a binary file for input*/ +static bool w_open_in(word_file *f); /*open a word file for input*/ +static FILE *open_out(const char *file_name, const char *file_mode); /* \TeX\ Live*/ +static bool a_open_out(alpha_file *f); /*open a text file for output*/ +static bool b_open_out(byte_file *f); /*open a binary file for output*/ +#ifdef @!INIT +static bool w_open_out(word_file *f); /*open a word file for output*/ +#endif + +@ Files can be closed with the \ph\ routine `|pascal_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 |pascal_close| operation makes the corresponding external file appear +on the user's area, ready to be read. + +These procedures should not generate error messages if a file is +being closed before it has been successfully opened. + +@p static void a_close(alpha_file *f) /*close a text file*/ +{@+pascal_close((*f)); +} +@# +static void b_close(byte_file *f) /*close a binary file*/ +{@+pascal_close((*f)); +} +@# +static void w_close(word_file *f) /*close a word file*/ +{@+pascal_close((*f)); +} + +@ Binary input and output are done with \PASCAL's ordinary |get| and |put| +procedures, so we don't have to make any other special arrangements for +binary~I/O. Text output is also easy to do with standard \PASCAL\ routines. +The treatment of text input is more difficult, however, because +of the necessary translation to |ASCII_code| values. +\TeX's conventions should be efficient, and they should +blend nicely with the user's operating environment. + +@ Input from text files is read one line at a time, using a routine called +|input_ln|. This function is defined in terms of global variables called +|buffer|, |first|, and |last| that will be described in detail later; for +now, it suffices for us to know that |buffer| is an array of |ASCII_code| +values, and that |first| and |last| are indices into this array +representing the beginning and ending of a line of text. + +@<Glob...@>= +static ASCII_code @!buffer[buf_size+1]; /*lines of characters being read*/ +static int @!first; /*the first unused position in |buffer|*/ +static int @!last; /*end of the line just input to |buffer|*/ +static int @!max_buf_stack; /*largest index used in |buffer|*/ + +@ The |input_ln| function brings the next line of input from the specified +file into available positions of the buffer array and returns the value +|true|, unless the file has already been entirely read, in which case it +returns |false| and sets |last=first|. In general, the |ASCII_code| +numbers that represent the next line of the file are input into +|buffer[first]|, |buffer[first+1]|, \dots, |buffer[last-1]|; and the +global variable |last| is set equal to |first| plus the length of the +line. Trailing blanks are removed from the line; thus, either |last==first| +(in which case the line was entirely blank) or |buffer[last-1]!=' '|. + +An overflow error is given, however, if the normal actions of |input_ln| +would make |last >= buf_size|; this is done so that other parts of \TeX\ +can safely look at the contents of |buffer[last+1]| without overstepping +the bounds of the |buffer| array. Upon entry to |input_ln|, the condition +|first < buf_size| will always hold, so that there is always room for an +``empty'' line. + +The variable |max_buf_stack|, which is used to keep track of how large +the |buf_size| parameter must be to accommodate the present job, is +also kept up to date by |input_ln|. + +If the |bypass_eoln| parameter is |true|, |input_ln| will do a |get| +before looking at the first character of the line; this skips over +an |eoln| that was in |f.d|. The procedure does not do a |get| when it +reaches the end of the line; therefore it can be used to acquire input +from the user's terminal as well as from ordinary text files. + +Standard \PASCAL\ says that a file should have |eoln| immediately +before |eof|, but \TeX\ needs only a weaker restriction: If |eof| +occurs in the middle of a line, the system function |eoln| should return +a |true| result (even though |f.d| will be undefined). + +Since the inner loop of |input_ln| is part of \TeX's ``inner loop''---each +character of input comes in at this place---it is wise to reduce system +overhead by making use of special routines that read in an entire array +of characters at once, if such routines are available. The following +code uses standard \PASCAL\ to illustrate what needs to be done, but +finer tuning is often possible at well-developed \PASCAL\ sites. +@^inner loop@> + +@p static bool input_ln(alpha_file *f, bool @!bypass_eoln) + /*inputs the next line or returns |false|*/ +{@+int last_nonblank; /*|last| with trailing blanks removed*/ +if (bypass_eoln) if (!eof((*f))) get((*f)); + /*input the first character of the line into |f.d|*/ +last=first; /*cf.\ Matthew 19\thinspace:\thinspace30*/ +if (eof((*f))) return false; +else{@+last_nonblank=first; + while (!eoln((*f))) + {@+if (last >= max_buf_stack) + {@+max_buf_stack=last+1; + if (max_buf_stack==buf_size) + @<Report overflow of the input buffer, and abort@>; + } + buffer[last]=xord[(*f).d];get((*f));incr(last); + if (buffer[last-1]!=' ') last_nonblank=last; + } + last=last_nonblank;return true; + } +} + +@ 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...@>= +static alpha_file @!term_in; /*the terminal as an input file*/ +static alpha_file @!term_out; /*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 term_in.f=stdin /*open the terminal for text input*/ +@d t_open_out term_out.f=stdout /*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 fflush(term_out.f) /*empty the terminal output buffer*/ +@d clear_terminal fflush(term_in.f) /*clear the terminal input buffer*/ +@d wake_up_terminal do_nothing /*cancel the user's cancellation of output*/ + +@ We need a special routine to read the first line of \TeX\ input from +the user's terminal. This line is different because it is read before we +have opened the transcript file; there is sort of a ``chicken and +egg'' problem here. If the user types `\.{\\input paper}' on the first +line, or if some macro invoked by that line does such an \.{\\input}, +the transcript file will be named `\.{paper.log}'; but if no \.{\\input} +commands are performed during the first line of terminal input, the transcript +file will acquire its default name `\.{texput.log}'. (The transcript file +will not contain error messages generated by the first line before the +first \.{\\input} command.) +@.texput@> + +The first line is even more special if we are lucky enough to have an operating +system that treats \TeX\ differently from a run-of-the-mill \PASCAL\ object +program. It's nice to let the user start running a \TeX\ job by typing +a command line like `\.{tex paper}'; in such a case, \TeX\ will operate +as if the first line of input were `\.{paper}', i.e., the first line will +consist of the remainder of the command line, after the part that invoked +\TeX. + +The first line is special also because it may be read before \TeX\ has +input a format file. In such cases, normal error messages cannot yet +be given. The following code uses concepts that will be explained later. +(If the \PASCAL\ compiler does not support non-local |@!goto|\unskip, the +@^system dependencies@> +statement `|goto exit(0)|' should be replaced by something that +quietly terminates the program.) + +@<Report overflow of the input buffer, and abort@>= +if (format_ident==0) + {@+write_ln(term_out,"Buffer size exceeded!");exit(0); +@.Buffer size exceeded@> + } +else{@+cur_input.loc_field=first;cur_input.limit_field=last-1; + overflow("buffer size", buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> + } + +@ Different systems have different ways to get started. But regardless of +what conventions are adopted, the routine that initializes the terminal +should satisfy the following specifications: + +\yskip\textindent{1)}It should open file |term_in| for input from the + terminal. (The file |term_out| will already be open for output to the + terminal.) + +\textindent{2)}If the user has given a command line, this line should be + considered the first line of terminal input. Otherwise the + user should be prompted with `\.{**}', and the first line of input + should be whatever is typed in response. + +\textindent{3)}The first line of input, which might or might not be a + command line, should appear in locations |first| to |last-1| of the + |buffer| array. + +\textindent{4)}The global variable |loc| should be set so that the + character to be read next by \TeX\ is in |buffer[loc]|. This + character should not be blank, and we should have |loc < last|. + +\yskip\noindent(It may be necessary to prompt the user several times +before a non-blank line comes in. The prompt is `\.{**}' instead of the +later `\.*' because the meaning is slightly different: `\.{\\input}' need +not be typed immediately after~`\.{**}'.) + +@d loc cur_input.loc_field /*location of first unread character in |buffer|*/ + +@ The following routine calls |input_command_line| +to retrieve a possible command line. +@^system dependencies@> + +@p static bool init_terminal(void) /*gets the terminal input started*/ +{@+ +t_open_in; +if (input_command_line()) return true; /* \TeX\ Live */ +loop@+{@+wake_up_terminal;pascal_write(term_out,"**");update_terminal; +@.**@> + if (!input_ln(&term_in, true)) /*this shouldn't happen*/ + {@+write_ln(term_out); + pascal_write(term_out,"! End of file on the terminal... why?"); +@.End of file on the terminal@> + return false; + } + loc=first; + while ((loc < last)&&(buffer[loc]==' ')) incr(loc); + if (loc < last) + {@+return true; + /*return unless the line was all blank*/ + } + write_ln(term_out,"Please type the name of your input file."); + } +} + +@* String handling. +Control sequence names and diagnostic messages are variable-length strings +of eight-bit characters. Since \PASCAL\ does not have a well-developed string +mechanism, \TeX\ does all of its string processing by homegrown methods. + +Elaborate facilities for dynamic strings are not needed, so all of the +necessary operations can be handled with a simple data structure. +The array |str_pool| contains all of the (eight-bit) ASCII codes in all +of the strings, and the array |str_start| contains indices of the starting +points of each string. Strings are referred to by integer numbers, so that +string number |s| comprises the characters |str_pool[j]| for +|str_start[s] <= j < str_start[s+1]|. Additional integer variables +|pool_ptr| and |str_ptr| indicate the number of entries used so far +in |str_pool| and |str_start|, respectively; locations +|str_pool[pool_ptr]| and |str_start[str_ptr]| are +ready for the next string to be allocated. + +String numbers 0 to 255 are reserved for strings that correspond to single +ASCII characters. This is in accordance with the conventions of \.{WEB}, +@.WEB@> +which converts single-character strings into the ASCII code number of the +single character involved, while it converts other strings into integers +and builds a string pool file. Thus, when the string constant \.{"."} appears +in the program below, \.{WEB} converts it into the integer 46, which is the +ASCII code for a period, while \.{WEB} will convert a string like \.{"hello"} +into some integer greater than~255. String number 46 will presumably be the +single character `\..'; but some ASCII codes have no standard visible +representation, and \TeX\ sometimes needs to be able to print an arbitrary +ASCII character, so the first 256 strings are used to specify exactly what +should be printed for each of the 256 possibilities. + +Elements of the |str_pool| array must be ASCII codes that can actually +be printed; i.e., they must have an |xchr| equivalent in the local +character set. (This restriction applies only to preloaded strings, +not to those generated dynamically by the user.) + +Some \PASCAL\ compilers won't pack integers into a single byte unless the +integers lie in the range |-128 dotdot 127|. To accommodate such systems +we access the string pool only via macros that can easily be redefined. +@^system dependencies@> + +@d si(A) A /*convert from |ASCII_code| to |packed_ASCII_code|*/ +@d so(A) A /*convert from |packed_ASCII_code| to |ASCII_code|*/ + +@<Types...@>= +typedef int32_t pool_pointer; /*for variables that point into |str_pool|*/ +typedef int32_t str_number; /*for variables that point into |str_start|*/ +typedef uint8_t packed_ASCII_code; /*elements of |str_pool| array*/ + +@ @<Glob...@>= +static packed_ASCII_code @!str_pool[pool_size+1]; /*the characters*/ +static pool_pointer @!str_start[max_strings+1]; /*the starting pointers*/ +static pool_pointer @!pool_ptr; /*first unused position in |str_pool|*/ +static str_number @!str_ptr; /*number of the current string being created*/ +static pool_pointer @!init_pool_ptr; /*the starting value of |pool_ptr|*/ +static str_number @!init_str_ptr; /*the starting value of |str_ptr|*/ + +@ Several of the elementary string operations are performed using \.{WEB} +macros instead of \PASCAL\ procedures, because many of the +operations are done quite frequently and we want to avoid the +overhead of procedure calls. For example, here is +a simple macro that computes the length of a string. +@.WEB@> + +@d length(A) (str_start[A+1]-str_start[A]) /*the number of characters + in string number \#*/ + +@ The length of the current string is called |cur_length|: + +@d cur_length (pool_ptr-str_start[str_ptr]) + +@ Strings are created by appending character codes to |str_pool|. +The |append_char| macro, defined here, does not check to see if the +value of |pool_ptr| has gotten too high; this test is supposed to be +made before |append_char| is used. There is also a |flush_char| +macro, which erases the last character appended. + +To test if there is room to append |l| more characters to |str_pool|, +we shall write |str_room(l)|, which aborts \TeX\ and gives an +apologetic error message if there isn't enough room. + +@d append_char(A) /*put |ASCII_code| \# at the end of |str_pool|*/ +{@+str_pool[pool_ptr]=si(A);incr(pool_ptr); +} +@d flush_char decr(pool_ptr) /*forget the last character in the pool*/ +@d str_room(A) /*make sure that the pool hasn't overflowed*/ + {@+if (pool_ptr+A > pool_size) + overflow("pool size", pool_size-init_pool_ptr); +@:TeX capacity exceeded pool size}{\quad pool size@> + } + +@ Once a sequence of characters has been appended to |str_pool|, it +officially becomes a string when the function |make_string| is called. +This function returns the identification number of the new string as its +value. + +@p static str_number make_string(void) /*current string enters the pool*/ +{@+if (str_ptr==max_strings) + overflow("number of strings", max_strings-init_str_ptr); +@:TeX capacity exceeded number of strings}{\quad number of strings@> +incr(str_ptr);str_start[str_ptr]=pool_ptr; +return str_ptr-1; +} + +@ To destroy the most recently made string, we say |flush_string|. + +@d flush_string {@+decr(str_ptr);pool_ptr=str_start[str_ptr]; + } + +@ The following subroutine compares string |s| with another string of the +same length that appears in |buffer| starting at position |k|; +the result is |true| if and only if the strings are equal. +Empirical tests indicate that |str_eq_buf| is used in such a way that +it tends to return |true| about 80 percent of the time. + +@p static bool str_eq_buf(str_number @!s, int @!k) + /*test equality of strings*/ +{@+ /*loop exit*/ +pool_pointer j; /*running index*/ +bool @!result; /*result of comparison*/ +j=str_start[s]; +while (j < str_start[s+1]) + {@+if (so(str_pool[j])!=buffer[k]) + {@+result=false;goto not_found; + } + incr(j);incr(k); + } +result=true; +not_found: return result; +} + +@ Here is a similar routine, but it compares two strings in the string pool, +and it does not assume that they have the same length. + +@p static bool str_eq_str(str_number @!s, str_number @!t) + /*test equality of strings*/ +{@+ /*loop exit*/ +pool_pointer j, @!k; /*running indices*/ +bool @!result; /*result of comparison*/ +result=false; +if (length(s)!=length(t)) goto not_found; +j=str_start[s];k=str_start[t]; +while (j < str_start[s+1]) + {@+if (str_pool[j]!=str_pool[k]) goto not_found; + incr(j);incr(k); + } +result=true; +not_found: return result; +} +@t\4@>@<Declare \Prote\ procedures for strings@>@; + +@ The initial values of |str_pool|, |str_start|, |pool_ptr|, +and |str_ptr| are computed by the \.{INITEX} program, based in part +on the information that \.{WEB} has output while processing \TeX. +@.INITEX@> +@^string pool@> + +@p +static bool get_strings_started(void) /*initializes the string pool*/ +{@+ +int k, @!l; /*small indices or counters*/ +pool_ptr=0;str_ptr=0;str_start[0]=0; +@<Make the first 256 strings@>; +@<Add the empty string to the string pool@>; +return true; +} + +@ @d app_lc_hex(A) l=A; + if (l < 10) append_char(l+'0')@;@+else append_char(l-10+'a') + +@<Make the first 256...@>= +for (k=0; k<=255; k++) + {@+if ((@<Character |k| cannot be printed@>)) + {@+append_char('^');append_char('^'); + if (k < 0100) append_char(k+0100)@; + else if (k < 0200) append_char(k-0100)@; + else{@+app_lc_hex(k/16);app_lc_hex(k%16); + } + } + else append_char(k); + make_string(); + } + +@ 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[032]==@t\.{\'^^Z\'}@>|, +would like string 032 to be the single character 032 instead of the +three characters 0136, 0136, 0132 (\.{\^\^Z}). On the other hand, +even people with an extended character set will want to represent string +015 by \.{\^\^M}, since 015 is |carriage_return|; the idea is to +produce visible strings instead of tabs or line-feeds or carriage-returns +or bell-rings or characters that are treated anomalously in text files. + +Unprintable characters of codes 128--255 are, similarly, rendered +\.{\^\^80}--\.{\^\^ff}. + +The boolean expression defined here should be |true| unless \TeX\ +internal code number~|k| corresponds to a non-troublesome visible +symbol in the local character set. An appropriate formula for the +extended character set recommended in {\sl The \TeX book\/} would, for +example, be `|k in[0, 010 dotdot 012, 014, 015, 033, 0177 dotdot 0377]|'. +If character |k| cannot be printed, and |k < 0200|, then character |k+0100| or +|k-0100| must be printable; moreover, ASCII codes |[041 dotdot 046, +060 dotdot 071, 0136, 0141 dotdot 0146, 0160 dotdot 0171]| must be printable. +Thus, at least 80 printable characters are needed. +@:TeXbook}{\sl The \TeX book@> +@^character set dependencies@> +@^system dependencies@> + +@<Character |k| cannot be printed@>= + (k < ' ')||(k > '~') + +@ The |pool_file| variable is no longer needed and has been removed. + +Instead of reading the other strings from the \.{TEX.POOL} file, +it is sufficient here to add the empty string. +@<Add the empty string to the string pool@>= +make_string(); + +@ Without a string pool file there is no need for a pool check sum either. +But this is a convenient place to define the function |s_no| that will +add literal strings to the string pool at runtime, thereby obtaining their +string number. + +@p static int s_no(const char *str) +{@+ + if (str[0]==0) return empty_string; + if (str[1]==0) return str[0]; + str_room(strlen(str)); + while (*str!=0) append_char(*str++); + return make_string(); +} + +@ The function |s_no| is used in |initialize| and needs a forward +declaration. + +@<Forward declarations@>= +static int s_no(const char *str); + +@* On-line and off-line printing. +Messages that are sent to a user's terminal and to the transcript-log file +are produced by several `|print|' procedures. These procedures will +direct their output to a variety of places, based on the setting of +the global variable |selector|, which has the following possible +values: + +\yskip +\hang |term_and_log|, the normal setting, prints on the terminal and on the + transcript file. + +\hang |log_only|, prints only on the transcript file. + +\hang |term_only|, prints only on the terminal. + +\hang |no_print|, doesn't print at all. This is used only in rare cases + before the transcript file is open. + +\hang |pseudo|, puts output into a cyclic buffer that is used + by the |show_context| routine; when we get to that routine we shall discuss + the reasoning behind this curious mode. + +\hang |new_string|, appends the output to the current string in the + string pool. + +\hang 0 to 15, prints on one of the sixteen files for \.{\\write} output. + +\yskip +\noindent The symbolic names `|term_and_log|', etc., have been assigned +numeric codes that satisfy the convenient relations |no_print+1==term_only|, +|no_print+2==log_only|, |term_only+2==log_only+1==term_and_log|. + +Three additional global variables, |tally| and |term_offset| and +|file_offset|, record the number of characters that have been printed +since they were most recently cleared to zero. We use |tally| to record +the length of (possibly very long) stretches of printing; |term_offset| +and |file_offset|, on the other hand, keep track of how many characters +have appeared so far on the current line that has been output to the +terminal or to the transcript file, respectively. + +@d no_print 16 /*|selector| setting that makes data disappear*/ +@d term_only 17 /*printing is destined for the terminal only*/ +@d log_only 18 /*printing is destined for the transcript file only*/ +@d term_and_log 19 /*normal |selector| setting*/ +@d pseudo 20 /*special |selector| setting for |show_context|*/ +@d new_string 21 /*printing is deflected to the string pool*/ +@d max_selector 21 /*highest selector setting*/ + +@<Glob...@>= +static alpha_file @!log_file; /*transcript of \TeX\ session*/ +static int @!selector; /*where to print a message*/ +static int8_t @!dig[23]; /*digits in a number being output*/ +static int @!tally; /*the number of characters recently printed*/ +static int @!term_offset; + /*the number of characters on the current terminal line*/ +static int @!file_offset; + /*the number of characters on the current file line*/ +static ASCII_code @!trick_buf[error_line+1]; /*circular buffer for + pseudoprinting*/ +static int @!trick_count; /*threshold for pseudoprinting, explained later*/ +static int @!first_count; /*another variable for pseudoprinting*/ + +@ @<Initialize the output routines@>= +selector=term_only;tally=0;term_offset=0;file_offset=0; + +@ Macro abbreviations for output to the terminal and to the log file are +defined here for convenience. Some systems need special conventions +for terminal output, and it is possible to adhere to those conventions +by changing |wterm|, |wterm_ln|, and |wterm_cr| in this section. +@^system dependencies@> + +@<Basic printing procedures@>= +#define @[put(F)@] @[fwrite(&((F).d)@],@[sizeof((F).d),1,(F).f)@]@; +#define @[get(F)@] @[(void)!fread(&((F).d),sizeof((F).d),1,(F).f)@] + +#define @[pascal_close(F)@] @[fclose((F).f)@] +#define @[eof(F)@] @[feof((F).f)@] +#define @[eoln(F)@] @[((F).d=='\n'||eof(F))@] +#define @[erstat(F)@] @[((F).f==NULL?-1:ferror((F).f))@] + +#define @[pascal_read(F,X)@] @[((X)=(F).d,get(F))@] +#define @[read_ln(F)@] do get(F); while (!eoln(F)) + +#define @[pascal_write(F, FMT,...)@] @[fprintf(F.f,FMT,## __VA_ARGS__)@] +#define @[write_ln(F,...)@] @[pascal_write(F,__VA_ARGS__"\n")@] + +#define @[wterm(FMT,...)@] @[pascal_write(term_out,FMT, ## __VA_ARGS__)@] +#define @[wterm_ln(FMT,...)@] @[wterm(FMT "\n", ## __VA_ARGS__)@] +#define wterm_cr @[pascal_write(term_out,"\n")@] +#define @[wlog(FMT, ...)@] @[pascal_write(log_file,FMT, ## __VA_ARGS__)@] +#define @[wlog_ln(FMT, ...)@] @[wlog(FMT "\n", ## __VA_ARGS__)@] +#define wlog_cr @[pascal_write(log_file,"\n")@] + +@ To end a line of text output, we call |print_ln|. + +@<Basic print...@>= +static void print_ln(void) /*prints an end-of-line*/ +{@+switch (selector) { +case term_and_log: {@+wterm_cr;wlog_cr; + term_offset=0;file_offset=0; + } @+break; +case log_only: {@+wlog_cr;file_offset=0; + } @+break; +case term_only: {@+wterm_cr;term_offset=0; + } @+break; +case no_print: case pseudo: case new_string: do_nothing;@+break; +default:write_ln(write_file[selector]); +} @/ +} /*|tally| is not affected*/ + +@ The |print_char| procedure sends one character to the desired destination, +using the |xchr| array to map it into an external character compatible with +|input_ln|. All printing comes through |print_ln| or |print_char|. + +@<Basic printing...@>= +static void print_char(ASCII_code @!s) /*prints a single character*/ +{@+ +if (@<Character |s| is the current new-line character@>) + if (selector < pseudo) + {@+print_ln();return; + } +switch (selector) { +case term_and_log: {@+wterm("%c",xchr[s]);wlog("%c",xchr[s]); + incr(term_offset);incr(file_offset); + if (term_offset==max_print_line) + {@+wterm_cr;term_offset=0; + } + if (file_offset==max_print_line) + {@+wlog_cr;file_offset=0; + } + } @+break; +case log_only: {@+wlog("%c",xchr[s]);incr(file_offset); + if (file_offset==max_print_line) print_ln(); + } @+break; +case term_only: {@+wterm("%c",xchr[s]);incr(term_offset); + if (term_offset==max_print_line) print_ln(); + } @+break; +case no_print: do_nothing;@+break; +case pseudo: if (tally < trick_count) trick_buf[tally%error_line]=s;@+break; +case new_string: {@+if (pool_ptr < pool_size) append_char(s); + } @+break; /*we drop characters if the string space is full*/ +default:pascal_write(write_file[selector],"%c", xchr[s]); +} @/ +incr(tally); +} + +@ An entire string is output by calling |print|. Note that if we are outputting +the single standard ASCII character \.c, we could call |print('c')|, since +|'c'==99| is the number of a single-character string, as explained above. But +|print_char('c')| is quicker, so \TeX\ goes directly to the |print_char| +routine when it knows that this is safe. (The present implementation +assumes that it is always safe to print a visible ASCII character.) +@^system dependencies@> + +@<Basic print...@>= +static void print(char *s) /* the simple version */ +{ @+if (s == NULL) s="???"; /*this can't happen*/ + while (*s!=0) print_char(*s++);@+ +} + +static void printn(int @!s) /*prints string |s|*/ +{@+ +pool_pointer j; /*current character code position*/ +int @!nl; /*new-line character to restore*/ +if (s >= str_ptr) {print("???"); return;}/*this can't happen*/ +@.???@> +else if (s < 256) + if (s < 0) { print("???");return; } /*can't happen*/ + else{@+if (selector > pseudo) + {@+print_char(s);return; /*internal strings are not expanded*/ + } + if ((@<Character |s| is the current new-line character@>)) + if (selector < pseudo) + {@+print_ln();return; + } + nl=new_line_char;new_line_char=-1; + /*temporarily disable new-line character*/ + j=str_start[s]; + while (j < str_start[s+1]) + {@+print_char(so(str_pool[j]));incr(j); + } + new_line_char=nl;return; + } +j=str_start[s]; +while (j < str_start[s+1]) + {@+print_char(so(str_pool[j]));incr(j); + } +} + +@ Control sequence names, file names, and strings constructed with +\.{\\string} might contain |ASCII_code| values that can't +be printed using |print_char|. Therefore we use |slow_print| for them: + +@<Basic print...@>= +static void slow_print(int @!s) /*prints string |s|*/ +{@+pool_pointer j; /*current character code position*/ +if ((s >= str_ptr)||(s < 256)) printn(s); +else{@+j=str_start[s]; + while (j < str_start[s+1]) + {@+printn(so(str_pool[j]));incr(j); + } + } +} + +@ Here is the very first thing that \TeX\ prints: a headline that identifies +the version number and format package. The |term_offset| variable is temporarily +incorrect, but the discrepancy is not serious since we assume that this +part of the program is system dependent. +@^system dependencies@> + +According to the conventions of \TeX\ Live, + we print the |dump_name| if no format identifier is known. +@<Initialize the output...@>= +wterm("%s",banner); +if (format_ident==0) wterm_ln(" (preloaded format=%s)", dump_name); +else{@+slow_print(format_ident);print_ln(); + } +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...@>= +static void print_nl(char *@!s) /*prints string |s| at beginning of line*/ +{@+if (((term_offset > 0)&&(odd(selector)))||@| + ((file_offset > 0)&&(selector >= log_only))) print_ln(); +print(s); +} + +@ The procedure |print_esc| prints a string that is preceded by +the user's escape character (which is usually a backslash). + +@<Basic print...@>= +static void printn_esc(str_number @!s) /*prints escape character, then |s|*/ +{@+int c; /*the escape character code*/ +@<Set variable |c| to the current escape character@>; +if (c >= 0) if (c < 256) printn(c); +slow_print(s); +} + +static void print_esc(char *@!s) /*the fast way*/ +{@+int c; /*the escape character code*/ +@<Set variable |c| to the current escape character@>; +if (c >= 0) if (c < 256) printn(c); +print(s); +} + +@ An array of digits in the range |0 dotdot 15| is printed by |print_the_digs|. + +@<Basic print...@>= +static void print_the_digs(eight_bits @!k) + /*prints |dig[k-1]|$\,\ldots\,$|dig[0]|*/ +{@+while (k > 0) + {@+decr(k); + if (dig[k] < 10) print_char('0'+dig[k]); + else print_char('A'-10+dig[k]); + } +} + +@ 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 |%| or |/| +to negative arguments, since such operations are not implemented consistently +by all \PASCAL\ compilers. + +@<Basic print...@>= +static void print_int(int @!n) /*prints an integer in decimal form*/ +{@+int k; /*index to current digit; we assume that $\vert n\vert<10^{23}$*/ +int @!m; /*used to negate |n| in possibly dangerous cases*/ +k=0; +if (n < 0) + {@+print_char('-'); + if (n > -100000000) negate(n); + else{@+m=-1-n;n=m/10;m=(m%10)+1;k=1; + if (m < 10) dig[0]=m; + else{@+dig[0]=0;incr(n); + } + } + } +@/do@+{dig[k]=n%10;n=n/10;incr(k); +}@+ while (!(n==0)); +print_the_digs(k); +} + +@ Here is a trivial procedure to print two digits; it is usually called with +a parameter in the range |0 <= n <= 99|. + +@p static void print_two(int @!n) /*prints two least significant digits*/ +{@+n=abs(n)%100;print_char('0'+(n/10)); +print_char('0'+(n%10)); +} + +@ Hexadecimal printing of nonnegative integers is accomplished by |print_hex|. + +@p static void print_hex(int @!n) + /*prints a positive integer in hexadecimal form*/ +{@+int k; /*index to current digit; we assume that $0\le n<16^{22}$*/ +k=0;print_char('"'); +@/do@+{dig[k]=n%16;n=n/16;incr(k); +}@+ while (!(n==0)); +print_the_digs(k); +} + +@ Old versions of \TeX\ needed a procedure called |print_ASCII| whose function +is now subsumed by |print|. We retain the old name here as a possible aid to +future software arch\ae ologists. + +@d print_ASCII printn + +@ Roman numerals are produced by the |print_roman_int| routine. Readers +who like puzzles might enjoy trying to figure out how this tricky code +works; therefore no explanation will be given. Notice that 1990 yields +\.{mcmxc}, not \.{mxm}. + +@p static void print_roman_int(int @!n) +{@+ +pool_pointer j, @!k; /*mysterious indices into |mystery|*/ +nonnegative_integer @!u, @!v; /*mysterious numbers*/ +const char mystery[] ="m2d5c2l5x2v5i"; +j=0;v=1000; +loop@+{@+while (n >= v) + {@+print_char(so(mystery[j]));n=n-v; + } + if (n <= 0) return; /*nonpositive input produces no output*/ + k=j+2;u=v/(so(mystery[k-1])-'0'); + if (mystery[k-1]==si('2')) + {@+k=k+2;u=u/(so(mystery[k-1])-'0'); + } + if (n+u >= v) + {@+print_char(so(mystery[k]));n=n+u; + } + else{@+j=j+2;v=v/(so(mystery[j-1])-'0'); + } + } +} + +@ The |print| subroutine will not print a string that is still being +created. The following procedure will. + +@p static void print_current_string(void) /*prints a yet-unmade string*/ +{@+pool_pointer j; /*points to current character code*/ +j=str_start[str_ptr]; +while (j < pool_ptr) + {@+print_char(so(str_pool[j]));incr(j); + } +} + +@ 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(A) {@+wake_up_terminal;print(A);term_input(); + } /*prints a string and gets a line of input*/ + +@p static void term_input(void) /*gets a line from the terminal*/ +{@+int k; /*index into |buffer|*/ +update_terminal; /*now the user sees the prompt for sure*/ +if (!input_ln(&term_in, true)) 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) for (k=first; k<=last-1; k++) printn(buffer[k]); +print_ln();incr(selector); /*restore previous status*/ +} + +@* Reporting errors. +When something anomalous is detected, \TeX\ typically does something like this: +$$\vbox{\halign{#\hfil\cr +|print_err("Something anomalous has been detected");|\cr +|help3("This is the first line of my offer to help.")|\cr +|("This is the second line. I'm trying to")|\cr +|("explain the best way for you to proceed.");|\cr +|error;|\cr}}$$ +A two-line help message would be given using |help2|, etc.; these informal +helps should use simple vocabulary that complements the words used in the +official error message that was printed. (Outside the U.S.A., the help +messages should preferably be translated into the local vernacular. Each +line of help is at most 60 characters long, in the present implementation, +so that |max_print_line| will not be exceeded.) + +The |print_err| procedure supplies a `\.!' before the official message, +and makes sure that the terminal is awake if a stop is going to occur. +The |error| procedure supplies a `\..' after the official message, then it +shows the location of the error; and if |interaction==error_stop_mode|, +it also enters into a dialog with the user, during which time the help +message may be printed. +@^system dependencies@> + +@<Error handling...@>= +static void print_err(char *s) +{@+if (interaction==error_stop_mode) wake_up_terminal; + if (filelineerrorstylep) print_file_line(); /* \TeX\ Live */ + else print_nl("! "); + print(s); +} + +@ 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*/ + +@<Glob...@>= +static int @!interaction; /*current level of interaction*/ + +@ @<Set init...@>= +if (interaction_option<0) interaction=error_stop_mode; +else interaction=interaction_option; /* \TeX\ Live */ + +@ \TeX\ is careful not to call |error| when the print |selector| setting +might be unusual. The only possible values of |selector| at the time of +error messages are + +\yskip\hang|no_print| (when |interaction==batch_mode| + and |log_file| not yet open); + +\hang|term_only| (when |interaction > batch_mode| and |log_file| not yet open); + +\hang|log_only| (when |interaction==batch_mode| and |log_file| is open); + +\hang|term_and_log| (when |interaction > batch_mode| and |log_file| is open). + +@<Initialize the print |selector| based on |interaction|@>= +if (interaction==batch_mode) selector=no_print;@+else selector=term_only + +@ A global variable |deletions_allowed| is set |false| if the |get_next| +routine is active when |error| is called; this ensures that |get_next| +and related routines like |get_token| will never be called recursively. +A similar interlock is provided by |set_box_allowed|. +@^recursion@> + +The global variable |history| records the worst level of error that +has been detected. It has four possible values: |spotless|, |warning_issued|, +|error_message_issued|, and |fatal_error_stop|. + +Another global variable, |error_count|, is increased by one when an +|error| occurs without an interactive dialog, and it is reset to zero at +the end of every paragraph. If |error_count| reaches 100, \TeX\ decides +that there is no point in continuing further. + +@d spotless 0 /*|history| value when nothing has been amiss yet*/ +@d warning_issued 1 /*|history| value when |begin_diagnostic| has been called*/ +@d error_message_issued 2 /*|history| value when |error| has been called*/ +@d fatal_error_stop 3 /*|history| value when termination was premature*/ + +@<Glob...@>= +static bool @!deletions_allowed; /*is it safe for |error| to call |get_token|?*/ +static bool @!set_box_allowed; /*is it safe to do a \.{\\setbox} assignment?*/ +static int @!history; /*has the source input been clean so far?*/ +static int @!error_count; /*the number of scrolled errors since the + last paragraph ended*/ + +@ The value of |history| is initially |fatal_error_stop|, but it will +be changed to |spotless| if \TeX\ survives the initialization process. + +@<Set init...@>= +deletions_allowed=true;set_box_allowed=true; +error_count=0; /*|history| is initialized elsewhere*/ + +@ Since errors can be detected almost anywhere in \TeX, we want to declare the +error procedures near the beginning of the program. But the error procedures +in turn use some other procedures, which need to be declared |forward| +before we get to |error| itself. + +It is possible for |error| to be called recursively if some error arises +when |get_token| is being used to delete a token, and/or if some fatal error +occurs while \TeX\ is trying to fix a non-fatal one. But such recursion +@^recursion@> +is never more than two levels deep. + +@<Error handling...@>= +static void normalize_selector(void);@/ +static void get_token(void);@/ +static void term_input(void);@/ +static void show_context(void);@/ +static void begin_file_reading(void);@/ +static void open_log_file(void);@/ +static void close_files_and_terminate(void);@/ +static void clear_for_error_prompt(void);@/ +static void give_err_help(void);@/ +#ifdef @!DEBUG +static void debug_help(void); +#else +#define debug_help() do_nothing +#endif + +@ Individual lines of help are recorded in the array |help_line|, which +contains entries in positions |0 dotdot(help_ptr-1)|. They should be printed +in reverse order, i.e., with |help_line[0]| appearing last. + +@d hlp1(A) help_line[0]=A;@+} +@d hlp2(A, B) help_line[1]=A;help_line[0]=B;@+} +@d hlp3(A, B, C) help_line[2]=A;help_line[1]=B;help_line[0]=C;@+} +@d hlp4(A, B, C, D) help_line[3]=A;help_line[2]=B;help_line[1]=C;help_line[0]=D;@+} +@d hlp5(A, B, C, D, E) help_line[4]=A;help_line[3]=B;help_line[2]=C;help_line[1]=D;help_line[0]=E;@+} +@d hlp6(A, B, C, D, E, F) help_line[5]=A;help_line[4]=B;help_line[3]=C;help_line[2]=D;help_line[1]=E;help_line[0]=F;@+} +@d help0 help_ptr=0 /*sometimes there might be no help*/ +@d help1(A) @+{@+help_ptr=1;hlp1(A) /*use this with one help line*/ +@d help2(A, B) @+{@+help_ptr=2;hlp2(A, B) /*use this with two help lines*/ +@d help3(A, B, C) @+{@+help_ptr=3;hlp3(A, B, C) /*use this with three help lines*/ +@d help4(A, B, C, D) @+{@+help_ptr=4;hlp4(A, B, C, D) /*use this with four help lines*/ +@d help5(A, B, C, D, E) @+{@+help_ptr=5;hlp5(A, B, C, D, E) /*use this with five help lines*/ +@d help6(A, B, C, D, E, F) @+{@+help_ptr=6;hlp6(A, B, C, D, E, F) /*use this with six help lines*/ + +@<Glob...@>= +static char *@!help_line[6]; /*helps for the next |error|*/ +static int @!help_ptr; /*the number of help lines present*/ +static bool @!use_err_help; /*should the |err_help| list be shown?*/ + +@ @<Set init...@>= +help_ptr=0;use_err_help=false; + +@ The |jump_out| procedure just cuts across all active procedure levels and +goes to |end_of_TEX|. This is the only nontrivial |@!goto| statement in the +whole program. It is used when there is no recovery from a particular error. + +Some \PASCAL\ compilers do not implement non-local |goto| statements. +@^system dependencies@> +In such cases the body of |jump_out| should simply be +`|close_files_and_terminate|;\thinspace' followed by a call on some system +procedure that quietly terminates the program. + +@<Error hand...@>= +static void jump_out(void) +{@+ close_files_and_terminate(); exit(0); +} + +@ Here now is the general |error| routine. + +@<Error hand...@>= +static void error(void) /*completes the job of error reporting*/ +{@+ +ASCII_code c; /*what the user types*/ +int @!s1, @!s2, @!s3, @!s4; + /*used to save global variables when deleting tokens*/ +if (history < error_message_issued) history=error_message_issued; +print_char('.');show_context(); +if (interaction==error_stop_mode) + @<Get user's advice and |return|@>; +incr(error_count); +if (error_count==100) + {@+print_nl("(That makes 100 errors; please try again.)"); +@.That makes 100 errors...@> + history=fatal_error_stop;jump_out(); + } +@<Put help message on the transcript file@>; +} + +@ @<Get user's advice...@>= +loop@+{@+resume: if (interaction!=error_stop_mode) return; + clear_for_error_prompt();prompt_input("? "); +@.?\relax@> + if (last==first) return; + c=buffer[first]; + if (c >= 'a') c=c+'A'-'a'; /*convert to uppercase*/ + @<Interpret code |c| and |return| if done@>; + } + +@ It is desirable to provide an `\.E' option here that gives the user +an easy way to return from \TeX\ to the system editor, with the offending +line ready to be edited. But such an extension requires some system +wizardry, so the present implementation simply types out the name of the +file that should be +edited and the relevant line number. +@^system dependencies@> + +There is a secret `\.D' option available when the debugging routines haven't +been commented~out. +@^debugging@> + +@<Interpret code |c| and |return| if done@>= +switch (c) { +case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + case '8': case '9': if (deletions_allowed) + @<Delete \(c)|c-"0"| tokens and |goto resume|@>@;@+break; +@t\4\4@>@; +#ifdef @!DEBUG +case 'D': {@+debug_help();goto resume;@+} +#endif +case 'E': if (base_ptr > 0) if (input_stack[base_ptr].name_field >= 256) + {@+print_nl("You want to edit file "); +@.You want to edit file x@> + slow_print(input_stack[base_ptr].name_field); + print(" at line ");print_int(line); + interaction=scroll_mode;jump_out(); + } @+break; +case 'H': @<Print the help information and |goto resume|@>@; +case 'I': @<Introduce new material from the terminal and |return|@>@; +case 'Q': case 'R': case 'S': @<Change the interaction level and |return|@>@; +case 'X': {@+interaction=scroll_mode;jump_out(); + } @+break; +default:do_nothing; +} @/ +@<Print the menu of available options@>@; + +@ @<Print the menu...@>= +{@+print("Type <return> to proceed, S to scroll future error messages,");@/ +@.Type <return> to proceed...@> +print_nl("R to run without stopping, Q to run quietly,");@/ +print_nl("I to insert something, "); +if (base_ptr > 0) if (input_stack[base_ptr].name_field >= 256) + print("E to edit your file,"); +if (deletions_allowed) + print_nl("1 or ... or 9 to ignore the next 1 to 9 tokens of input,"); +print_nl("H for help, X to quit."); +} + +@ Here the author of \TeX\ apologizes for making use of the numerical +relation between |'Q'|, |'R'|, |'S'|, and the desired interaction settings +|batch_mode|, |nonstop_mode|, |scroll_mode|. +@^Knuth, Donald Ervin@> + +@<Change the interaction...@>= +{@+error_count=0;interaction=batch_mode+c-'Q'; +print("OK, entering "); +switch (c) { +case 'Q': {@+print_esc("batchmode");decr(selector); + } @+break; +case 'R': print_esc("nonstopmode");@+break; +case 'S': print_esc("scrollmode"); +} /*there are no other cases*/ +print("...");print_ln();update_terminal;return; +} + +@ When the following code is executed, |buffer[(first+1)dotdot(last-1)]| may +contain the material inserted by the user; otherwise another prompt will +be given. In order to understand this part of the program fully, you need +to be familiar with \TeX's input stacks. + +@<Introduce new material...@>= +{@+begin_file_reading(); /*enter a new syntactic level for terminal input*/ + /*now |state==mid_line|, so an initial blank space will count as a blank*/ +if (last > first+1) + {@+loc=first+1;buffer[first]=' '; + } +else{@+prompt_input("insert>");loc=first; +@.insert>@> + } +first=last; +cur_input.limit_field=last-1; /*no |end_line_char| ends this line*/ +return; +} + +@ We allow deletion of up to 99 tokens at a time. + +@<Delete \(c)|c-"0"| tokens...@>= +{@+s1=cur_tok;s2=cur_cmd;s3=cur_chr;s4=align_state; +align_state=1000000;OK_to_interrupt=false; +if ((last > first+1)&&(buffer[first+1] >= '0')&&(buffer[first+1] <= '9')) + c=c*10+buffer[first+1]-'0'*11; +else c=c-'0'; +while (c > 0) + {@+get_token(); /*one-level recursive call of |error| is possible*/ + decr(c); + } +cur_tok=s1;cur_cmd=s2;cur_chr=s3;align_state=s4;OK_to_interrupt=true; +help2("I have just deleted some text, as you asked.",@/ +"You can now delete more, or insert, or whatever."); +show_context();goto resume; +} + +@ @<Print the help info...@>= +{@+if (use_err_help) + {@+give_err_help();use_err_help=false; + } +else{@+if (help_ptr==0) + help2("Sorry, I don't know how to help in this situation.",@/ + @t\kern1em@>"Maybe you should try asking a human?"); + @/do@+{decr(help_ptr);print(help_line[help_ptr]);print_ln(); + }@+ while (!(help_ptr==0)); + } +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 resume; +} + +@ @<Put help message on the transcript file@>= +if (interaction > batch_mode) decr(selector); /*avoid terminal output*/ +if (use_err_help) + {@+print_ln();give_err_help(); + } +else while (help_ptr > 0) + {@+decr(help_ptr);print_nl(help_line[help_ptr]); + } +print_ln(); +if (interaction > batch_mode) incr(selector); /*re-enable terminal output*/ +print_ln() + +@ A dozen or so error messages end with a parenthesized integer, so we +save a teeny bit of program space by declaring the following procedure: + +@p static void int_error(int @!n) +{@+print(" (");print_int(n);print_char(')');error(); +} + +@ 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 static void normalize_selector(void) +{@+if (log_opened) selector=term_and_log; +else selector=term_only; +if (job_name==0) open_log_file(); +if (interaction==batch_mode) decr(selector); +} + +@ The following procedure prints \TeX's last words before dying. + +@d succumb {@+if (interaction==error_stop_mode) + interaction=scroll_mode; /*no more interaction*/ + if (log_opened) error(); + if (interaction > batch_mode) debug_help(); + history=fatal_error_stop;jump_out(); /*irrecoverable error*/ + } + +@<Error hand...@>= +static void fatal_error(char *@!s) /*prints |s|, and that's it*/ +{@+normalize_selector();@/ +print_err("Emergency stop");help1(s);succumb; +@.Emergency stop@> +} + +@ Here is the most dreaded error message. + +@<Error hand...@>= +static void overflow(char *@!s, int @!n) /*stop due to finiteness*/ +{@+normalize_selector(); +print_err("TeX capacity exceeded, sorry ["); +@.TeX capacity exceeded ...@> +print(s);print_char('=');print_int(n);print_char(']'); +help2("If you really absolutely need more capacity,",@/ + "you can ask a wizard to enlarge me."); +succumb; +} + +@ The program might sometime run completely amok, at which point there is +no choice but to stop. If no previous error has been detected, that's bad +news; a message is printed that is really intended for the \TeX\ +maintenance person instead of the user (unless the user has been +particularly diabolical). The index entries for `this can't happen' may +help to pinpoint the problem. +@^dry rot@> + +@<Error hand...@>= +static void confusion(char *@!s) + /*consistency check violated; |s| tells where*/ +{@+normalize_selector(); +if (history < error_message_issued) + {@+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"); + } +else{@+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."); + } +succumb; +} + +@ Users occasionally want to interrupt \TeX\ while it's running. +If the \PASCAL\ runtime system allows this, one can implement +a routine that sets the global variable |interrupt| to some nonzero value +when such an interrupt is signalled. Otherwise there is probably at least +a way to make |interrupt| nonzero using the \PASCAL\ debugger. +@^system dependencies@> +@^debugging@> + +@d check_interrupt {@+if (interrupt!=0) pause_for_instructions(); + } + +@<Global...@>= +static int @!interrupt; /*should \TeX\ pause for instructions?*/ +static bool @!OK_to_interrupt; /*should interrupts be observed?*/ + +@ @<Set init...@>= +interrupt=0;OK_to_interrupt=true; + +@ When an interrupt has been detected, the program goes into its +highest interaction level and lets the user have nearly the full flexibility of +the |error| routine. \TeX\ checks for interrupts only at times when it is +safe to do this. + +@p static void pause_for_instructions(void) +{@+if (OK_to_interrupt) + {@+interaction=error_stop_mode; + if ((selector==log_only)||(selector==no_print)) + incr(selector); + print_err("Interruption"); +@.Interruption@> + help3("You rang?",@/ + "Try to insert an instruction for me (e.g., `I\\showlists'),",@/ + "unless you just want to quit by typing `X'."); + deletions_allowed=false;error();deletions_allowed=true; + interrupt=0; + } +} + +@* Arithmetic with scaled dimensions. +The principal computations performed by \TeX\ are done entirely in terms of +integers less than $2^{31}$ in magnitude; and divisions are done only when both +dividend and divisor are nonnegative. Thus, the arithmetic specified in this +program can be carried out in exactly the same way on a wide variety of +computers, including some small ones. Why? Because the arithmetic +calculations need to be spelled out precisely in order to guarantee that +\TeX\ will produce identical output on different machines. If some +quantities were rounded differently in different implementations, we would +find that line breaks and even page breaks might occur in different places. +Hence the arithmetic of \TeX\ has been designed with care, and systems that +claim to be implementations of \TeX82 should follow precisely the +@:TeX82}{\TeX82@> +calculations as they appear in the present program. + +(Actually there are three places where \TeX\ uses |/| with a possibly negative +numerator. These are harmless; see |/| in the index. Also if the user +sets the \.{\\time} or the \.{\\year} to a negative value, some diagnostic +information will involve negative-numerator division. The same remarks +apply for |%| as well as for |/|.) + +@ Here is a routine that calculates half of an integer, using an +unambiguous convention with respect to signed odd numbers. + +@p static int half(int @!x) +{@+if (odd(x)) return(x+1)/2; +else return x/2; +} + +@ Fixed-point arithmetic is done on {\sl scaled integers\/} that are multiples +of $2^{-16}$. In other words, a binary point is assumed to be sixteen bit +positions from the right end of a binary computer word. + +@d unity 0200000 /*$2^{16}$, represents 1.00000*/ +@d two 0400000 /*$2^{17}$, represents 2.00000*/ + +@<Types...@>= +typedef int scaled; /*this type is used for scaled integers*/ +typedef int32_t nonnegative_integer; /*$0\le x<2^{31}$*/ +typedef int8_t small_number; /*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 static scaled round_decimals(small_number @!k) + /*converts a decimal fraction*/ +{@+int a; /*the accumulator*/ +a=0; +while (k > 0) + {@+decr(k);a=(a+dig[k]*two)/10; + } +return(a+1)/2; +} + +@ Conversely, here is a procedure analogous to |print_int|. If the output +of this procedure is subsequently read by \TeX\ and converted by the +|round_decimals| routine above, it turns out that the original value will +be reproduced exactly; the ``simplest'' such decimal number is output, +but there is always at least one digit following the decimal point. + +The invariant relation in the \&{repeat} loop is that a sequence of +decimal digits yet to be printed will yield the original number if and only if +they form a fraction~$f$ in the range $s-\delta\le 10\cdot2^{16}f<s$. +We can stop if and only if $f=0$ satisfies this condition; the loop will +terminate before $s$ can possibly become zero. + +@p static void print_scaled(scaled @!s) /*prints scaled real, rounded to five + digits*/ +{@+scaled delta; /*amount of allowable inaccuracy*/ +if (s < 0) + {@+print_char('-');negate(s); /*print the sign, if negative*/ + } +print_int(s/unity); /*print the integer part*/ +print_char('.'); +s=10*(s%unity)+5;delta=10; +@/do@+{if (delta > unity) s=s+0100000-50000; /*round the last digit*/ +print_char('0'+(s/unity));s=10*(s%unity);delta=delta*10; +}@+ while (!(s <= delta)); +} + +@ Physical sizes that a \TeX\ user specifies for portions of documents are +represented internally as scaled points. Thus, if we define an `sp' (scaled +@^sp@> +point) as a unit equal to $2^{-16}$ printer's points, every dimension +inside of \TeX\ is an integer number of sp. There are exactly +4,736,286.72 sp per inch. Users are not allowed to specify dimensions +larger than $2^{30}-1$ sp, which is a distance of about 18.892 feet (5.7583 +meters); two such quantities can be added without overflow on a 32-bit +computer. + +The present implementation of \TeX\ does not check for overflow when +@^overflow in arithmetic@> +dimensions are added or subtracted. This could be done by inserting a +few dozen tests of the form `\ignorespaces|if (x >= 010000000000)| +\\{report\_overflow}', but the chance of overflow is so remote that +such tests do not seem worthwhile. + +\TeX\ needs to do only a few arithmetic operations on scaled quantities, +other than addition and subtraction, and the following subroutines do most of +the work. A single computation might use several subroutine calls, and it is +desirable to avoid producing multiple error messages in case of arithmetic +overflow; so the routines set the global variable |arith_error| to |true| +instead of reporting errors directly to the user. Another global variable, +|rem|, holds the remainder after a division. + +@<Glob...@>= +static bool @!arith_error; /*has arithmetic overflow occurred recently?*/ +static scaled @!rem; /*amount subtracted to get an exact division*/ + +@ The first arithmetical subroutine we need computes $nx+y$, where |x| +and~|y| are |scaled| and |n| is an integer. We will also use it to +multiply integers. + +@d nx_plus_y(A, B, C) mult_and_add(A, B, C, 07777777777) +@d mult_integers(A, B) mult_and_add(A, B, 0, 017777777777) + +@p static scaled mult_and_add(int @!n, scaled @!x, scaled @!y, scaled @!max_answer) +{@+if (n < 0) + {@+negate(x);negate(n); + } +if (n==0) return y; +else if (((x <= (max_answer-y)/n)&&(-x <= (max_answer+y)/n))) + return n*x+y; +else{@+arith_error=true;return 0; + } +} + +@ We also need to divide scaled dimensions by integers. + +@p static scaled x_over_n(scaled @!x, int @!n) +{@+bool negative; /*should |rem| be negated?*/ +scaled x_over_n; +negative=false; +if (n==0) + {@+arith_error=true;x_over_n=0;rem=x; + } +else{@+if (n < 0) + {@+negate(x);negate(n);negative=true; + } + if (x >= 0) + {@+x_over_n=x/n;rem=x%n; + } + else{@+x_over_n=-((-x)/n);rem=-((-x)%n); + } + } +if (negative) negate(rem); +return x_over_n;} + +@ Then comes the multiplication of a scaled number by a fraction |n/(double)d|, +where |n| and |d| are nonnegative integers | <= @t$2^{16}$@>| and |d| is +positive. It would be too dangerous to multiply by~|n| and then divide +by~|d|, in separate operations, since overflow might well occur; and it +would be too inaccurate to divide by |d| and then multiply by |n|. Hence +this subroutine simulates 1.5-precision arithmetic. + +@p static scaled xn_over_d(scaled @!x, int @!n, int @!d) +{@+bool positive; /*was |x >= 0|?*/ +nonnegative_integer @!t, @!u, @!v; /*intermediate quantities*/ +scaled xn_over_d; +if (x >= 0) positive=true; +else{@+negate(x);positive=false; + } +t=(x%0100000)*n; +u=(x/0100000)*n+(t/0100000); +v=(u%d)*0100000+(t%0100000); +if (u/d >= 0100000) arith_error=true; +else u=0100000*(u/d)+(v/d); +if (positive) + {@+xn_over_d=u;rem=v%d; + } +else{@+xn_over_d=-u;rem=-(v%d); + } +return xn_over_d;} + +@ The next subroutine is used to compute the ``badness'' of glue, when a +total~|t| is supposed to be made from amounts that sum to~|s|. According +to {\sl The \TeX book}, the badness of this situation is $100(t/s)^3$; +however, badness is simply a heuristic, so we need not squeeze out the +last drop of accuracy when computing it. All we really want is an +approximation that has similar properties. +@:TeXbook}{\sl The \TeX book@> + +The actual method used to compute the badness is easier to read from the +program than to describe in words. It produces an integer value that is a +reasonably close approximation to $100(t/s)^3$, and all implementations +of \TeX\ should use precisely this method. Any badness of $2^{13}$ or more is +treated as infinitely bad, and represented by 10000. + +It is not difficult to prove that $$\hbox{|badness(t+1, s) >= badness(t, s) + >= badness(t, s+1)|}.$$ The badness function defined here is capable of +computing at most 1095 distinct values, but that is plenty. + +@d inf_bad 10000 /*infinitely bad value*/ + +@p @<Declare \Prote\ arithmetic routines@>@/ +static halfword badness(scaled @!t, scaled @!s) /*compute badness, given |t >= 0|*/ +{@+int r; /*approximation to $\alpha t/s$, where $\alpha^3\approx + 100\cdot2^{18}$*/ +if (t==0) return 0; +else if (s <= 0) return inf_bad; +else{@+if (t <= 7230584) r=(t*297)/s; /*$297^3=99.94\times2^{18}$*/ + else if (s >= 1663497) r=t/(s/297); + else r=t; + if (r > 1290) return inf_bad; /*$1290^3<2^{31}<1291^3$*/ + else return(r*r*r+0400000)/01000000; + } /*that was $r^3/2^{18}$, rounded to the nearest integer*/ +} + +@ When \TeX\ ``packages'' a list into a box, it needs to calculate the +proportionality ratio by which the glue inside the box should stretch +or shrink. This calculation does not affect \TeX's decision making, +so the precise details of rounding, etc., in the glue calculation are not +of critical importance for the consistency of results on different computers. + +We shall use the type |glue_ratio| for such proportionality ratios. +A glue ratio should take the same amount of memory as an +|int| (usually 32 bits) if it is to blend smoothly with \TeX's +other data structures. Thus |glue_ratio| should be equivalent to +|short_real| in some implementations of \PASCAL. Alternatively, +it is possible to deal with glue ratios using nothing but fixed-point +arithmetic; see {\sl TUGboat \bf3},1 (March 1982), 10--27. (But the +routines cited there must be modified to allow negative glue ratios.) +@^system dependencies@> + +@d set_glue_ratio_zero(A) A=0.0 /*store the representation of zero ratio*/ +@d set_glue_ratio_one(A) A=1.0 /*store the representation of unit ratio*/ +@d unfix(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/ +@d fix(A) ((glue_ratio)(A)) /*convert from |double| to type |glue_ratio|*/ +@d float_constant(A) ((double)(A)) /*convert |int| constant to |double|*/ + +@<Types...@>= +#if __SIZEOF_FLOAT__==4 +typedef float float32_t; +#else +#error @=float type must have size 4@> +#endif +typedef float @!glue_ratio; /*one-word representation of a glue expansion factor*/ + +@* Packed data. +In order to make efficient use of storage space, \TeX\ bases its major data +structures on a |memory_word|, which contains either a (signed) integer, +possibly scaled, or a (signed) |glue_ratio|, or a small number of +fields that are one half or one quarter of the size used for storing +integers. + +If |x| is a variable of type |memory_word|, it contains up to four +fields that can be referred to as follows: +$$\vbox{\halign{\hfil#&#\hfil&#\hfil\cr +|x|&.|i|&(an |int|)\cr +|x|&.|sc|\qquad&(a |scaled| integer)\cr +|x|&.|gr|&(a |glue_ratio|)\cr +|x.hh.lh|, |x.hh|&.|rh|&(two halfword fields)\cr +|x.hh.b0|, |x.hh.b1|, |x.hh|&.|rh|&(two quarterword fields, one halfword + field)\cr +|x.qqqq.b0|, |x.qqqq.b1|, |x.qqqq|&.|b2|, |x.qqqq.b3|\hskip-100pt + &\qquad\qquad\qquad(four quarterword fields)\cr}}$$ +This is somewhat cumbersome to write, and not very readable either, but +macros will be used to make the notation shorter and more transparent. +The \PASCAL\ code below gives a formal definition of |memory_word| and +its subsidiary types, using packed variant records. \TeX\ makes no +assumptions about the relative positions of the fields within a word. + +Since we are assuming 32-bit integers, a halfword must contain at least +16 bits, and a quarterword must contain at least 8 bits. +@^system dependencies@> +But it doesn't hurt to have more bits; for example, with enough 36-bit +words you might be able to have |mem_max| as large as 262142, which is +eight times as much memory as anybody had during the first four years of +\TeX's existence. + +N.B.: Valuable memory space will be dreadfully wasted unless \TeX\ is compiled +by a \PASCAL\ that packs all of the |memory_word| variants into +the space of a single integer. This means, for example, that |glue_ratio| +words should be |short_real| instead of |double| on some computers. Some +\PASCAL\ compilers will pack an integer whose subrange is `|0 dotdot 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 dotdot 127|'. + +The present implementation tries to accommodate as many variations as possible, +so it makes few assumptions. If integers having the subrange +`|min_quarterword dotdot max_quarterword|' can be packed into a quarterword, +and if integers having the subrange `|min_halfword dotdot 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 65535 /*largest allowable value in a |quarterword|*/ +@d min_halfword 0 /*smallest allowable value in a |halfword|*/ +@d max_halfword 0x3FFFFFFF /*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''...@>= +#ifdef @!INIT +if ((mem_min!=mem_bot)||(mem_max!=mem_top)) bad=10; +#endif +@;@/ +if ((mem_min > mem_bot)||(mem_max < mem_top)) bad=10; +if ((min_quarterword > 0)||(max_quarterword < 127)) bad=11; +if ((min_halfword > 0)||(max_halfword < 32767)) bad=12; +if ((min_quarterword < min_halfword)||@| + (max_quarterword > max_halfword)) bad=13; +if ((mem_min < min_halfword)||(mem_max >= max_halfword)||@| + (mem_bot-mem_min > max_halfword+1)) bad=14; +if ((font_base < min_quarterword)||(font_max > max_quarterword)) bad=15; +if (font_max > font_base+256) bad=16; +if ((save_size > max_halfword)||(max_strings > max_halfword)) bad=17; +if (buf_size > max_halfword) bad=18; +if (max_quarterword-min_quarterword < 255) bad=19; + +@ The operation of adding or subtracting |min_quarterword| occurs quite +frequently in \TeX, so it is convenient to abbreviate this operation +by using the macros |qi| and |qo| for input and output to and from +quarterword format. + +The inner loop of \TeX\ will run faster with respect to compilers +that don't optimize expressions like `|x+0|' and `|x-0|', if these +macros are simplified in the obvious way when |min_quarterword==0|. +@^inner loop@>@^system dependencies@> + +@d qi(A) A+min_quarterword + /*to put an |eight_bits| item into a quarterword*/ +@d qo(A) A-min_quarterword + /*to take an |eight_bits| item out of a quarterword*/ +@d hi(A) A+min_halfword + /*to put a sixteen-bit item into a halfword*/ +@d ho(A) A-min_halfword + /*to take a sixteen-bit item from a halfword*/ + +@ The reader should study the following definitions closely: +@^system dependencies@> + +@d sc i /*|scaled| data is equivalent to |int|*/ + +@<Types...@>= +typedef uint16_t quarterword; /*1/4 of a word*/ +typedef int32_t halfword; /*1/2 of a word*/ +typedef int8_t two_choices; /*used when there are two variants in a record*/ +typedef int8_t four_choices; /*used when there are four variants in a record*/ +typedef uint64_t fullword; +typedef struct { @;@/ + halfword @!rh; + union { + halfword @!lh; + struct { quarterword @!b0;quarterword @!b1;} ; + };} two_halves; +typedef struct { @;@/ + quarterword @!b0; + quarterword @!b1; + quarterword @!b2; + quarterword @!b3; + } four_quarters; +typedef struct { @;@/ + union { + fullword @!w; + int @!i; + glue_ratio @!gr; + two_halves @!hh; + four_quarters @!qqqq; + };} memory_word; +typedef struct {@+FILE *f;@+memory_word@,d;@+} word_file; + +@ 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 +#ifdef @!DEBUG +static void print_word(memory_word @!w) + /*prints |w| in all ways*/ +{@+print_int(w.i);print_char(' ');@/ +print_scaled(w.sc);print_char(' ');@/ +print_scaled(round(unity*unfix(w.gr)));print_ln();@/ +@^real multiplication@> +print_int(w.hh.lh);print_char('=');print_int(w.hh.b0);print_char(':'); +print_int(w.hh.b1);print_char(';');print_int(w.hh.rh);print_char(' ');@/ +print_int(w.qqqq.b0);print_char(':');print_int(w.qqqq.b1);print_char(':'); +print_int(w.qqqq.b2);print_char(':');print_int(w.qqqq.b3); +} +#endif + +@* Dynamic memory allocation. +The \TeX\ system does nearly all of its own memory allocation, so that it +can readily be transported into environments that do not have automatic +facilities for strings, garbage collection, etc., and so that it can be in +control of what error messages the user receives. The dynamic storage +requirements of \TeX\ are handled by providing a large array |mem| in +which consecutive blocks of words are used as nodes by the \TeX\ routines. + +Pointer variables are indices into this array, or into another array +called |eqtb| that will be explained later. A pointer variable might +also be a special flag that lies outside the bounds of |mem|, so we +allow pointers to assume any |halfword| value. The minimum halfword +value represents a null pointer. \TeX\ does not assume that |mem[null]| exists. + +@s pointer int +@d pointer halfword /*a flag or a location in |mem| or |eqtb|*/ +@d null min_halfword /*the null pointer*/ + +@<Glob...@>= +static pointer @!temp_ptr; /*a pointer variable for occasional emergency use*/ + +@ The |mem| array is divided into two regions that are allocated separately, +but the dividing line between these two regions is not fixed; they grow +together until finding their ``natural'' size in a particular job. +Locations less than or equal to |lo_mem_max| are used for storing +variable-length records consisting of two or more words each. This region +is maintained using an algorithm similar to the one described in exercise +2.5--19 of {\sl The Art of Computer Programming}. However, no size field +appears in the allocated nodes; the program is responsible for knowing the +relevant size when a node is freed. Locations greater than or equal to +|hi_mem_min| are used for storing one-word records; a conventional +\.{AVAIL} stack is used for allocation in this region. + +Locations of |mem| between |mem_bot| and |mem_top| may be dumped as part +of preloaded format files, by the \.{INITEX} preprocessor. +@.INITEX@> +Production versions of \TeX\ may extend the memory at both ends in order to +provide more space; locations between |mem_min| and |mem_bot| are always +used for variable-size nodes, and locations between |mem_top| and |mem_max| +are always used for single-word nodes. + +The key pointers that govern |mem| allocation have a prescribed order: +$$\advance\thickmuskip-2mu +\hbox{|null <= mem_min <= mem_bot < lo_mem_max < + hi_mem_min < mem_top <= mem_end <= mem_max|.}$$ + +Empirical tests show that the present implementation of \TeX\ tends to +spend about 9\pct! of its running time allocating nodes, and about 6\pct! +deallocating them after their use. + +@<Glob...@>= +static memory_word @!mem0[mem_max-mem_min+1], + *const @!mem = @!mem0-mem_min; /*the big dynamic storage area*/ +static pointer @!lo_mem_max; /*the largest location of variable-size memory in use*/ +static pointer @!hi_mem_min; /*the smallest location of one-word memory in use*/ + +@ In order to study the memory requirements of particular applications, it +is possible to prepare a version of \TeX\ that keeps track of current and +maximum memory usage. When code between the delimiters | +#ifdef @!STAT +| $\ldots$ +|@t\#\&{endif}@>| is not ``commented out,'' \TeX\ will run a bit slower but it will +report these statistics when |tracing_stats| is sufficiently large. + +@<Glob...@>= +static int @!var_used, @!dyn_used; /*how much memory is in use*/ +#ifdef @!STAT +#define incr_dyn_used @[incr(dyn_used)@] +#define decr_dyn_used @[decr(dyn_used)@] +#else +#define incr_dyn_used +#define decr_dyn_used +#endif + +@ 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(A) mem[A].hh.rh /*the |link| field of a memory word*/ +@d info(A) mem[A].hh.lh /*the |info| field of a memory word*/ + +@<Glob...@>= +static pointer @!avail; /*head of the list of available one-word nodes*/ +static pointer @!mem_end; /*the last one-word node used in |mem|*/ + +@ If memory is exhausted, it might mean that the user has forgotten +a right brace. We will define some procedures later that try to help +pinpoint the trouble. + +@p @<Declare the procedure called |show_token_list|@>@/ +@<Declare the procedure called |runaway|@>@; + +@ The function |get_avail| returns a pointer to a new one-word node whose +|link| field is null. However, \TeX\ will halt if there is no more room left. +@^inner loop@> + +If the available-space list is empty, i.e., if |avail==null|, +we try first to increase |mem_end|. If that cannot be done, i.e., if +|mem_end==mem_max|, we try to decrease |hi_mem_min|. If that cannot be +done, i.e., if |hi_mem_min==lo_mem_max+1|, we have to quit. + +@p static pointer get_avail(void) /*single-word node allocation*/ +{@+pointer p; /*the new node being got*/ +p=avail; /*get top location in the |avail| stack*/ +if (p!=null) avail=link(avail); /*and pop it off*/ +else if (mem_end < mem_max) /*or go into virgin territory*/ + {@+incr(mem_end);p=mem_end; + } +else{@+decr(hi_mem_min);p=hi_mem_min; + if (hi_mem_min <= lo_mem_max) + {@+runaway(); /*if memory is exhausted, display possible runaway text*/ + overflow("main memory size", mem_max+1-mem_min); + /*quit; all one-word nodes are busy*/ +@:TeX capacity exceeded main memory size}{\quad main memory size@> + } + } +link(p)=null; /*provide an oft-desired initialization of the new node*/ +#ifdef @!STAT +incr(dyn_used); +#endif +@; /*maintain statistics*/ +return p; +} + +@ Conversely, a one-word node is recycled by calling |free_avail|. +This routine is part of \TeX's ``inner loop,'' so we want it to be fast. +@^inner loop@> + +@d free_avail(A) /*single-word node liberation*/ + {@+link(A)=avail;avail=A; + decr_dyn_used; + } + +@ There's also a |fast_get_avail| routine, which saves the procedure-call +overhead at the expense of extra programming. This routine is used in +the places that would otherwise account for the most calls of |get_avail|. +@^inner loop@> + +@d fast_get_avail(A) @t@>@;@/ + {@+A=avail; /*avoid |get_avail| if possible, to save time*/ + if (A==null) A=get_avail(); + else{@+avail=link(A);link(A)=null; + incr_dyn_used; + } + } + +@ The procedure |flush_list(p)| frees an entire linked list of +one-word nodes that starts at position |p|. +@^inner loop@> + +@p static void flush_list(pointer @!p) /*makes list of single-word nodes + available*/ +{@+pointer @!q, @!r; /*list traversers*/ +if (p!=null) + {@+r=p; + @/do@+{q=r;r=link(r); +#ifdef @!STAT +decr(dyn_used); +#endif + }@+ while (!(r==null)); /*now |q| is the last node on the list*/ + link(q)=avail;avail=p; + } +} + +@ 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(A) (link(A)==empty_flag) /*tests for empty node*/ +@d node_size(A) info(A) /*the size field in empty variable-size nodes*/ +@d llink(A) info(A+1) /*left link in doubly-linked list of empty nodes*/ +@d rlink(A) link(A+1) /*right link in doubly-linked list of empty nodes*/ + +@<Glob...@>= +static pointer @!rover; /*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 static pointer get_node(int @!s) /*variable-size node allocation*/ +{@+ +pointer p; /*the node currently under inspection*/ +pointer @!q; /*the node physically after node |p|*/ +int @!r; /*the newly allocated node, or a candidate for this honor*/ +int @!t; /*temporary register*/ +restart: p=rover; /*start at some free node in the ring*/ +@/do@+{@<Try to allocate within node |p| and its physical successors, and +|goto found| if allocation was possible@>; +@^inner loop@> +p=rlink(p); /*move to the next node in the ring*/ +}@+ while (!(p==rover)); /*repeat until the whole list has been traversed*/ +if (s==010000000000) + {@+return max_halfword; + } +if (lo_mem_max+2 < hi_mem_min) if (lo_mem_max+2 <= mem_bot+max_halfword) + @<Grow more variable-size memory and |goto restart|@>; +overflow("main memory size", mem_max+1-mem_min); + /*sorry, nothing satisfactory is left*/ +@:TeX capacity exceeded main memory size}{\quad main memory size@> +found: link(r)=null; /*this node is now nonempty*/ +#ifdef @!STAT +var_used=var_used+s; /*maintain usage statistics*/ +#endif +@;@/ +return r; +} + +@ The lower part of |mem| grows by 1000 words at a time, unless +we are very close to going under. When it grows, we simply link +a new node into the available-space list. This method of controlled +growth helps to keep the |mem| usage consecutive when \TeX\ is +implemented on ``virtual memory'' systems. +@^virtual memory@> + +@<Grow more variable-size memory and |goto restart|@>= +{@+if (hi_mem_min-lo_mem_max >= 1998) t=lo_mem_max+1000; +else t=lo_mem_max+1+(hi_mem_min-lo_mem_max)/2; + /*|lo_mem_max+2 <= t < hi_mem_min|*/ +p=llink(rover);q=lo_mem_max;rlink(p)=q;llink(rover)=q;@/ +if (t > mem_bot+max_halfword) t=mem_bot+max_halfword; +rlink(q)=rover;llink(q)=p;link(q)=empty_flag;node_size(q)=t-lo_mem_max;@/ +lo_mem_max=t;link(lo_mem_max)=null;info(lo_mem_max)=null; +rover=q;goto restart; +} + +@ Empirical tests show that the routine in this section performs a +node-merging operation about 0.75 times per allocation, on the average, +after which it finds that |r > p+1| about 95\pct! of the time. + +@<Try to allocate...@>= +q=p+node_size(p); /*find the physical successor*/ +@^inner loop@> +while (is_empty(q)) /*merge node |p| with node |q|*/ + {@+t=rlink(q); + if (q==rover) rover=t; + llink(t)=llink(q);rlink(llink(q))=t;@/ + q=q+node_size(q); + } +r=q-s; +if (r > p+1) @<Allocate from the top of node |p| and |goto found|@>; +if (r==p) if (rlink(p)!=p) + @<Allocate entire node |p| and |goto found|@>; +node_size(p)=q-p /*reset the size in case it grew*/ + +@ @<Allocate from the top...@>= +{@+node_size(p)=r-p; /*store the remaining size*/ +@^inner loop@> +rover=p; /*start searching here next time*/ +goto found; +} + +@ Here we delete node |p| from the ring, and let |rover| rove around. + +@<Allocate entire...@>= +{@+rover=rlink(p);t=llink(p); +llink(rover)=t;rlink(t)=rover; +goto found; +} + +@ Conversely, when some variable-size node |p| of size |s| is no longer needed, +the operation |free_node(p, s)| will make its words available, by inserting +|p| as a new empty node just before where |rover| now points. +@^inner loop@> + +@p static void free_node(pointer @!p, halfword @!s) /*variable-size node + liberation*/ +{@+pointer q; /*|llink(rover)|*/ +node_size(p)=s;link(p)=empty_flag; +q=llink(rover);llink(p)=q;rlink(p)=rover; /*set both links*/ +llink(rover)=p;rlink(q)=p; /*insert |p| into the ring*/ +#ifdef @!STAT +var_used=var_used-s; +#endif +@; /*maintain statistics*/ +} + +@ Just before \.{INITEX} writes out the memory, it sorts the doubly linked +available space list. The list is probably very short at such times, so a +simple insertion sort is used. The smallest available location will be +pointed to by |rover|, the next-smallest by |rlink(rover)|, etc. + +@p +#ifdef @!INIT +static void sort_avail(void) /*sorts the available variable-size nodes + by location*/ +{@+pointer p, @!q, @!r; /*indices into |mem|*/ +pointer @!old_rover; /*initial |rover| setting*/ +p=get_node(010000000000); /*merge adjacent free areas*/ +p=rlink(rover);rlink(rover)=max_halfword;old_rover=rover; +while (p!=old_rover) @<Sort \(p)|p| into the list starting at |rover| and +advance |p| to |rlink(p)|@>; +p=rover; +while (rlink(p)!=max_halfword) + {@+llink(rlink(p))=p;p=rlink(p); + } +rlink(p)=rover;llink(rover)=p; +} +#endif + +@ The following |while | loop is guaranteed to +terminate, since the list that starts at +|rover| ends with |max_halfword| during the sorting procedure. + +@<Sort \(p)|p|...@>= +if (p < rover) + {@+q=p;p=rlink(q);rlink(q)=rover;rover=q; + } +else{@+q=rover; + while (rlink(q) < p) q=rlink(q); + r=rlink(p);rlink(p)=rlink(q);rlink(q)=p;p=r; + } + +@* Data structures for boxes and their friends. +From the computer's standpoint, \TeX's chief mission is to create +horizontal and vertical lists. We shall now investigate how the elements +of these lists are represented internally as nodes in the dynamic memory. + +A horizontal or vertical list is linked together by |link| fields in +the first word of each node. Individual nodes represent boxes, glue, +penalties, or special things like discretionary hyphens; because of this +variety, some nodes are longer than others, and we must distinguish different +kinds of nodes. We do this by putting a `|type|' field in the first word, +together with the link and an optional `|subtype|'. + +@d type(A) mem[A].hh.b0 /*identifies what kind of node this is*/ +@d subtype(A) mem[A].hh.b1 /*secondary identification in some cases*/ + +@ A |@!char_node|, which represents a single character, is the most important +kind of node because it accounts for the vast majority of all boxes. +Special precautions are therefore taken to ensure that a |char_node| does +not take up much memory space. Every such node is one word long, and in fact +it is identifiable by this property, since other kinds of nodes have at least +two words, and they appear in |mem| locations less than |hi_mem_min|. +This makes it possible to omit the |type| field in a |char_node|, leaving +us room for two bytes that identify a |font| and a |character| within +that font. + +Note that the format of a |char_node| allows for up to 256 different +fonts and up to 256 characters per font; but most implementations will +probably limit the total number of fonts to fewer than 75 per job, +and most fonts will stick to characters whose codes are +less than 128 (since higher codes +are more difficult to access on most keyboards). + +Extensions of \TeX\ intended for oriental languages will need even more +than $256\times256$ possible characters, when we consider different sizes +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> +and styles of type. It is suggested that Chinese and Japanese fonts be +handled by representing such characters in two consecutive |char_node| +entries: The first of these has |font==font_base|, and its |link| points +to the second; +the second identifies the font and the character dimensions. +The saving feature about oriental characters is that most of them have +the same box dimensions. The |character| field of the first |char_node| +is a ``\\{charext}'' that distinguishes between graphic symbols whose +dimensions are identical for typesetting purposes. (See the \MF\ manual.) +Such an extension of \TeX\ would not be difficult; further details are +left to the reader. + +In order to make sure that the |character| code fits in a quarterword, +\TeX\ adds the quantity |min_quarterword| to the actual code. + +Character nodes appear only in horizontal lists, never in vertical lists. + +@d is_char_node(A) (A >= hi_mem_min) + /*does the argument point to a |char_node|?*/ +@d font(A) type(A) /*the font code in a |char_node|*/ +@d character(A) subtype(A) /*the character code in a |char_node|*/ + +@ An |hlist_node| stands for a box that was made from a horizontal list. +Each |hlist_node| is seven words long, and contains the following fields +(in addition to the mandatory |type| and |link|, which we shall not +mention explicitly when discussing the other node types): The |height| and +|width| and |depth| are scaled integers denoting the dimensions of the +box. There is also a |shift_amount| field, a scaled integer indicating +how much this box should be lowered (if it appears in a horizontal list), +or how much it should be moved to the right (if it appears in a vertical +list). There is a |list_ptr| field, which points to the beginning of the +list from which this box was fabricated; if |list_ptr| is |null|, the box +is empty. Finally, there are three fields that represent the setting of +the glue: |glue_set(p)| is a word of type |glue_ratio| that represents +the proportionality constant for glue setting; |glue_sign(p)| is +|stretching| or |shrinking| or |normal| depending on whether or not the +glue should stretch or shrink or remain rigid; and |glue_order(p)| +specifies the order of infinity to which glue setting applies (|normal|, +|fil|, |fill|, or |filll|). The |subtype| field is not used. + +@d hlist_node 0 /*|type| of hlist nodes*/ +@d box_node_size 7 /*number of words to allocate for a box node*/ +@d width_offset 1 /*position of |width| field in a box node*/ +@d depth_offset 2 /*position of |depth| field in a box node*/ +@d height_offset 3 /*position of |height| field in a box node*/ +@d width(A) mem[A+width_offset].sc /*width of the box, in sp*/ +@d depth(A) mem[A+depth_offset].sc /*depth of the box, in sp*/ +@d height(A) mem[A+height_offset].sc /*height of the box, in sp*/ +@d shift_amount(A) mem[A+4].sc /*repositioning distance, in sp*/ +@d list_offset 5 /*position of |list_ptr| field in a box node*/ +@d list_ptr(A) link(A+list_offset) /*beginning of the list inside the box*/ +@d glue_order(A) subtype(A+list_offset) /*applicable order of infinity*/ +@d glue_sign(A) type(A+list_offset) /*stretching or shrinking*/ +@d normal 0 /*the most common case when several cases are named*/ +@d stretching 1 /*glue setting applies to the stretch components*/ +@d shrinking 2 /*glue setting applies to the shrink components*/ +@d glue_offset 6 /*position of |glue_set| in a box node*/ +@d glue_set(A) mem[A+glue_offset].gr + /*a word of type |glue_ratio| for glue setting*/ + +@ The |new_null_box| function returns a pointer to an |hlist_node| in +which all subfields have the values corresponding to `\.{\\hbox\{\}}'. +(The |subtype| field is set to |min_quarterword|, for historic reasons +that are no longer relevant.) + +@p static pointer new_null_box(void) /*creates a new box node*/ +{@+pointer p; /*the new node*/ +p=get_node(box_node_size);type(p)=hlist_node; +subtype(p)=min_quarterword; +width(p)=0;depth(p)=0;height(p)=0;shift_amount(p)=0;list_ptr(p)=null; +glue_sign(p)=normal;glue_order(p)=normal;set_glue_ratio_zero(glue_set(p)); +return p; +} + +@ A |vlist_node| is like an |hlist_node| in all respects except that it +contains a vertical list. + +@d vlist_node 1 /*|type| of vlist nodes*/ + +@ A |rule_node| stands for a solid black rectangle; it has |width|, +|depth|, and |height| fields just as in an |hlist_node|. However, if +any of these dimensions is $-2^{30}$, the actual value will be determined +by running the rule up to the boundary of the innermost enclosing box. +This is called a ``running dimension.'' The |width| is never running in +an hlist; the |height| and |depth| are never running in a~vlist. + +@d rule_node 2 /*|type| of rule nodes*/ +@d rule_node_size 4 /*number of words to allocate for a rule node*/ +@d null_flag -010000000000 /*$-2^{30}$, signifies a missing item*/ +@d is_running(A) (A==null_flag) /*tests for a running dimension*/ + +@ A new rule node is delivered by the |new_rule| function. It +makes all the dimensions ``running,'' so you have to change the +ones that are not allowed to run. + +@p static pointer new_rule(void) +{@+pointer p; /*the new node*/ +p=get_node(rule_node_size);type(p)=rule_node; +subtype(p)=0; /*the |subtype| is not used*/ +width(p)=null_flag;depth(p)=null_flag;height(p)=null_flag; +return p; +} + +@ Insertions are represented by |ins_node| records, where the |subtype| +indicates the corresponding box number. For example, `\.{\\insert 250}' +leads to an |ins_node| whose |subtype| is |250+min_quarterword|. +The |height| field of an |ins_node| is slightly misnamed; it actually holds +the natural height plus depth of the vertical list being inserted. +The |depth| field holds the |split_max_depth| to be used in case this +insertion is split, and the |split_top_ptr| points to the corresponding +|split_top_skip|. The |float_cost| field holds the |floating_penalty| that +will be used if this insertion floats to a subsequent page after a +split insertion of the same class. There is one more field, the +|ins_ptr|, which points to the beginning of the vlist for the insertion. + +@d ins_node 3 /*|type| of insertion nodes*/ +@d ins_node_size 5 /*number of words to allocate for an insertion*/ +@d float_cost(A) mem[A+1].i /*the |floating_penalty| to be used*/ +@d ins_ptr(A) info(A+4) /*the vertical list to be inserted*/ +@d split_top_ptr(A) link(A+4) /*the |split_top_skip| to be used*/ + +@ A |mark_node| has a |mark_ptr| field that points to the reference count +of a token list that contains the user's \.{\\mark} text. +In addition there is a |mark_class| field that contains the mark class. + +@d mark_node 4 /*|type| of a mark node*/ +@d small_node_size 2 /*number of words to allocate for most node types*/ +@d mark_ptr(A) link(A+1) /*head of the token list for a mark*/ +@d mark_class(A) info(A+1) /*the mark class*/ + +@ An |adjust_node|, which occurs only in horizontal lists, +specifies material that will be moved out into the surrounding +vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' +operation. The |adjust_ptr| field points to the vlist containing this +material. + +@d adjust_node 5 /*|type| of an adjust node*/ +@d adjust_ptr(A) mem[A+1].i + /*vertical list to be moved out of horizontal list*/ + +@ A |ligature_node|, which occurs only in horizontal lists, specifies +a character that was fabricated from the interaction of two or more +actual characters. The second word of the node, which is called the +|lig_char| word, contains |font| and |character| fields just as in a +|char_node|. The characters that generated the ligature have not been +forgotten, since they are needed for diagnostic messages and for +hyphenation; the |lig_ptr| field points to a linked list of character +nodes for all original characters that have been deleted. (This list +might be empty if the characters that generated the ligature were +retained in other nodes.) + +The |subtype| field is 0, plus 2 and/or 1 if the original source of the +ligature included implicit left and/or right boundaries. + +@d ligature_node 6 /*|type| of a ligature node*/ +@d lig_char(A) A+1 /*the word where the ligature is to be found*/ +@d lig_ptr(A) link(lig_char(A)) /*the list of characters*/ + +@ The |new_ligature| function creates a ligature node having given +contents of the |font|, |character|, and |lig_ptr| fields. We also have +a |new_lig_item| function, which returns a two-word node having a given +|character| field. Such nodes are used for temporary processing as ligatures +are being created. + +@p static pointer new_ligature(quarterword @!f, quarterword @!c, pointer @!q) +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=ligature_node; +font(lig_char(p))=f;character(lig_char(p))=c;lig_ptr(p)=q; +subtype(p)=0;return p; +} +@# +static pointer new_lig_item(quarterword @!c) +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);character(p)=c;lig_ptr(p)=null; +return p; +} + +@ A |disc_node|, which occurs only in horizontal lists, specifies a +``dis\-cretion\-ary'' line break. If such a break occurs at node |p|, the text +that starts at |pre_break(p)| will precede the break, the text that starts at +|post_break(p)| will follow the break, and text that appears in the next +|replace_count(p)| nodes will be ignored. For example, an ordinary +discretionary hyphen, indicated by `\.{\\-}', yields a |disc_node| with +|pre_break| pointing to a |char_node| containing a hyphen, |post_break==null|, +and |replace_count==0|. All three of the discretionary texts must be +lists that consist entirely of character, kern, box, rule, and ligature nodes. + +If |pre_break(p)==null|, the |ex_hyphen_penalty| will be charged for this +break. Otherwise the |hyphen_penalty| will be charged. The texts will +actually be substituted into the list by the line-breaking algorithm if it +decides to make the break, and the discretionary node will disappear at +that time; thus, the output routine sees only discretionaries that were +not chosen. + +@d disc_node 7 /*|type| of a discretionary node*/ +@d replace_count(A) subtype(A) /*how many subsequent nodes to replace*/ +@d pre_break(A) llink(A) /*text that precedes a discretionary break*/ +@d post_break(A) rlink(A) /*text that follows a discretionary break*/ + +@p static pointer new_disc(void) /*creates an empty |disc_node|*/ +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=disc_node; +replace_count(p)=0;pre_break(p)=null;post_break(p)=null; +return p; +} + +@ A |whatsit_node| is a wild card reserved for extensions to \TeX. The +|subtype| field in its first word says what `\\{whatsit}' it is, and +implicitly determines the node size (which must be 2 or more) and the +format of the remaining words. When a |whatsit_node| is encountered +in a list, special actions are invoked; knowledgeable people who are +careful not to mess up the rest of \TeX\ are able to make \TeX\ do new +things by adding code at the end of the program. For example, there +might be a `\TeX nicolor' extension to specify different colors of ink, +@^extensions to \TeX@> +and the whatsit node might contain the desired parameters. + +The present implementation of \TeX\ treats the features associated with +`\.{\\write}' and `\.{\\special}' as if they were extensions, in order to +illustrate how such routines might be coded. We shall defer further +discussion of extensions until the end of this program. + +@d whatsit_node 8 /*|type| of special extension nodes*/ + +@ A |math_node|, which occurs only in horizontal lists, appears before and +after mathematical formulas. The |subtype| field is |before| before the +formula and |after| after it. There is a |width| field, which represents +the amount of surrounding space inserted by \.{\\mathsurround}. + +@d math_node 9 /*|type| of a math node*/ +@d before 0 /*|subtype| for math node that introduces a formula*/ +@d after 1 /*|subtype| for math node that winds up a formula*/ + +@p static pointer new_math(scaled @!w, small_number @!s) +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=math_node; +subtype(p)=s;width(p)=w;return p; +} + +@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, +|rule_node|, |ins_node|, |mark_node|, |adjust_node|, |ligature_node|, +|disc_node|, |whatsit_node|, and |math_node| are at the low end of the +type codes, by permitting a break at glue in a list if and only if the +|type| of the previous node is less than |math_node|. Furthermore, a +node is discarded after a break if its type is |math_node| or~more. + +@d precedes_break(A) (type(A) < math_node) +@d non_discardable(A) (type(A) < math_node) + +@ A |glue_node| represents glue in a list. However, it is really only +a pointer to a separate glue specification, since \TeX\ makes use of the +fact that many essentially identical nodes of glue are usually present. +If |p| points to a |glue_node|, |glue_ptr(p)| points to +another packet of words that specify the stretch and shrink components, etc. + +Glue nodes also serve to represent leaders; the |subtype| is used to +distinguish between ordinary glue (which is called |normal|) and the three +kinds of leaders (which are called |a_leaders|, |c_leaders|, and |x_leaders|). +The |leader_ptr| field points to a rule node or to a box node containing the +leaders; it is set to |null| in ordinary glue nodes. + +Many kinds of glue are computed from \TeX's ``skip'' parameters, and +it is helpful to know which parameter has led to a particular glue node. +Therefore the |subtype| is set to indicate the source of glue, whenever +it originated as a parameter. We will be defining symbolic names for the +parameter numbers later (e.g., |line_skip_code==0|, |baseline_skip_code==1|, +etc.); it suffices for now to say that the |subtype| of parametric glue +will be the same as the parameter number, plus~one. + +In math formulas there are two more possibilities for the |subtype| in a +glue node: |mu_glue| denotes an \.{\\mskip} (where the units are scaled \.{mu} +instead of scaled \.{pt}); and |cond_math_glue| denotes the `\.{\\nonscript}' +feature that cancels the glue node immediately following if it appears +in a subscript. + +@d glue_node 10 /*|type| of node that points to a glue specification*/ +@d cond_math_glue 98 /*special |subtype| to suppress glue in the next node*/ +@d mu_glue 99 /*|subtype| for math glue*/ +@d a_leaders 100 /*|subtype| for aligned leaders*/ +@d c_leaders 101 /*|subtype| for centered leaders*/ +@d x_leaders 102 /*|subtype| for expanded leaders*/ +@d glue_ptr(A) llink(A) /*pointer to a glue specification*/ +@d leader_ptr(A) rlink(A) /*pointer to box or rule node for leaders*/ + +@ A glue specification has a halfword reference count in its first word, +@^reference counts@> +representing |null| plus the number of glue nodes that point to it (less one). +Note that the reference count appears in the same position as +the |link| field in list nodes; this is the field that is initialized +to |null| when a node is allocated, and it is also the field that is flagged +by |empty_flag| in empty nodes. + +Glue specifications also contain three |scaled| fields, for the |width|, +|stretch|, and |shrink| dimensions. Finally, there are two one-byte +fields called |stretch_order| and |shrink_order|; these contain the +orders of infinity (|normal|, |fil|, |fill|, or |filll|) +corresponding to the stretch and shrink values. + +@d glue_spec_size 4 /*number of words to allocate for a glue specification*/ +@d glue_ref_count(A) link(A) /*reference count of a glue specification*/ +@d stretch(A) mem[A+2].sc /*the stretchability of this glob of glue*/ +@d shrink(A) mem[A+3].sc /*the shrinkability of this glob of glue*/ +@d stretch_order(A) type(A) /*order of infinity for stretching*/ +@d shrink_order(A) subtype(A) /*order of infinity for shrinking*/ +@d fil 1 /*first-order infinity*/ +@d fill 2 /*second-order infinity*/ +@d filll 3 /*third-order infinity*/ + +@<Types...@>= +typedef int8_t glue_ord; /*infinity to the 0, 1, 2, or 3 power*/ + +@ Here is a function that returns a pointer to a copy of a glue spec. +The reference count in the copy is |null|, because there is assumed +to be exactly one reference to the new specification. + +@p static pointer new_spec(pointer @!p) /*duplicates a glue specification*/ +{@+pointer q; /*the new spec*/ +q=get_node(glue_spec_size);@/ +mem[q]=mem[p];glue_ref_count(q)=null;@/ +width(q)=width(p);stretch(q)=stretch(p);shrink(q)=shrink(p); +return q; +} + +@ And here's a function that creates a glue node for a given parameter +identified by its code number; for example, +|new_param_glue(line_skip_code)| returns a pointer to a glue node for the +current \.{\\lineskip}. + +@p static pointer new_param_glue(small_number @!n) +{@+pointer p; /*the new node*/ +pointer @!q; /*the glue specification*/ +p=get_node(small_node_size);type(p)=glue_node;subtype(p)=n+1; +leader_ptr(p)=null;@/ +q=@<Current |mem| equivalent of glue parameter number |n|@>@t@>; +glue_ptr(p)=q;incr(glue_ref_count(q)); +return p; +} + +@ Glue nodes that are more or less anonymous are created by |new_glue|, +whose argument points to a glue specification. + +@p static pointer new_glue(pointer @!q) +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=glue_node;subtype(p)=normal; +leader_ptr(p)=null;glue_ptr(p)=q;incr(glue_ref_count(q)); +return p; +} + +@ Still another subroutine is needed: This one is sort of a combination +of |new_param_glue| and |new_glue|. It creates a glue node for one of +the current glue parameters, but it makes a fresh copy of the glue +specification, since that specification will probably be subject to change, +while the parameter will stay put. The global variable |temp_ptr| is +set to the address of the new spec. + +@p static pointer new_skip_param(small_number @!n) +{@+pointer p; /*the new node*/ +temp_ptr=new_spec(@<Current |mem| equivalent of glue parameter...@>); +p=new_glue(temp_ptr);glue_ref_count(temp_ptr)=null;subtype(p)=n+1; +return p; +} + +@ A |kern_node| has a |width| field to specify a (normally negative) +amount of spacing. This spacing correction appears in horizontal lists +between letters like A and V when the font designer said that it looks +better to move them closer together or further apart. A kern node can +also appear in a vertical list, when its `|width|' denotes additional +spacing in the vertical direction. The |subtype| is either |normal| (for +kerns inserted from font information or math mode calculations) or |explicit| +(for kerns inserted from \.{\\kern} and \.{\\/} commands) or |acc_kern| +(for kerns inserted from non-math accents) or |mu_glue| (for kerns +inserted from \.{\\mkern} specifications in math formulas). + +@d kern_node 11 /*|type| of a kern node*/ +@d explicit 1 /*|subtype| of kern nodes from \.{\\kern} and \.{\\/}*/ +@d acc_kern 2 /*|subtype| of kern nodes from accents*/ + +@ The |new_kern| function creates a kern node having a given width. + +@p static pointer new_kern(scaled @!w) +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=kern_node; +subtype(p)=normal; +width(p)=w; +return p; +} + +@ A |penalty_node| specifies the penalty associated with line or page +breaking, in its |penalty| field. This field is a fullword integer, but +the full range of integer values is not used: Any penalty | >= 10000| is +treated as infinity, and no break will be allowed for such high values. +Similarly, any penalty | <= -10000| is treated as negative infinity, and a +break will be forced. + +@d penalty_node 12 /*|type| of a penalty node*/ +@d inf_penalty inf_bad /*``infinite'' penalty value*/ +@d eject_penalty (-inf_penalty) /*``negatively infinite'' penalty value*/ +@d penalty(A) mem[A+1].i /*the added cost of breaking a list here*/ + +@ Anyone who has been reading the last few sections of the program will +be able to guess what comes next. + +@p static pointer new_penalty(int @!m) +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=penalty_node; +subtype(p)=0; /*the |subtype| is not used*/ +penalty(p)=m;return p; +} + +@ You might think that we have introduced enough node types by now. Well, +almost, but there is one more: An |unset_node| has nearly the same format +as an |hlist_node| or |vlist_node|; it is used for entries in \.{\\halign} +or \.{\\valign} that are not yet in their final form, since the box +dimensions are their ``natural'' sizes before any glue adjustment has been +made. The |glue_set| word is not present; instead, we have a |glue_stretch| +field, which contains the total stretch of order |glue_order| that is +present in the hlist or vlist being boxed. +Similarly, the |shift_amount| field is replaced by a |glue_shrink| field, +containing the total shrink of order |glue_sign| that is present. +The |subtype| field is called |span_count|; an unset box typically +contains the data for |qo(span_count)+1| columns. +Unset nodes will be changed to box nodes when alignment is completed. + +@d unset_node 13 /*|type| for an unset node*/ +@d glue_stretch(A) mem[A+glue_offset].sc /*total stretch in an unset node*/ +@d glue_shrink(A) shift_amount(A) /*total shrink in an unset node*/ +@d span_count(A) subtype(A) /*indicates the number of spanned columns*/ + +@ In fact, there are still more types coming. When we get to math formula +processing we will see that a |style_node| has |type==14|; and a number +of larger type codes will also be defined, for use in math mode only. + +@ Warning: If any changes are made to these data structure layouts, such as +changing any of the node sizes or even reordering the words of nodes, +the |copy_node_list| procedure and the memory initialization code +below may have to be changed. Such potentially dangerous parts of the +program are listed in the index under `data structure assumptions'. +@!@^data structure assumptions@> +However, other references to the nodes are made symbolically in terms of +the \.{WEB} macro definitions above, so that format changes will leave +\TeX's other algorithms intact. +@^system dependencies@> + +@* Memory layout. +Some areas of |mem| are dedicated to fixed usage, since static allocation is +more efficient than dynamic allocation when we can get away with it. For +example, locations |mem_bot| to |mem_bot+3| are always used to store the +specification for glue that is `\.{0pt plus 0pt minus 0pt}'. The +following macro definitions accomplish the static allocation by giving +symbolic names to the fixed positions. Static variable-size nodes appear +in locations |mem_bot| through |lo_mem_stat_max|, and static single-word nodes +appear in locations |hi_mem_stat_min| through |mem_top|, inclusive. It is +harmless to let |lig_trick| and |garbage| share the same location of |mem|. + +@d zero_glue mem_bot /*specification for \.{0pt plus 0pt minus 0pt}*/ +@d fil_glue zero_glue+glue_spec_size /*\.{0pt plus 1fil minus 0pt}*/ +@d fill_glue fil_glue+glue_spec_size /*\.{0pt plus 1fill minus 0pt}*/ +@d ss_glue fill_glue+glue_spec_size /*\.{0pt plus 1fil minus 1fil}*/ +@d fil_neg_glue ss_glue+glue_spec_size /*\.{0pt plus -1fil minus 0pt}*/ +@d lo_mem_stat_max fil_neg_glue+glue_spec_size-1 /*largest statically + allocated word in the variable-size |mem|*/ +@# +@d page_ins_head mem_top /*list of insertion data for current page*/ +@d contrib_head mem_top-1 /*vlist of items not yet on current page*/ +@d page_head mem_top-2 /*vlist for current page*/ +@d temp_head mem_top-3 /*head of a temporary list of some kind*/ +@d hold_head mem_top-4 /*head of a temporary list of another kind*/ +@d adjust_head mem_top-5 /*head of adjustment list returned by |hpack|*/ +@d active mem_top-7 /*head of active list in |line_break|, needs two words*/ +@d align_head mem_top-8 /*head of preamble list for alignments*/ +@d end_span mem_top-9 /*tail of spanned-width lists*/ +@d omit_template mem_top-10 /*a constant token list*/ +@d null_list mem_top-11 /*permanently empty list*/ +@d lig_trick mem_top-12 /*a ligature masquerading as a |char_node|*/ +@d garbage mem_top-12 /*used for scrap information*/ +@d backup_head mem_top-13 /*head of token list built by |scan_keyword|*/ +@d hi_mem_stat_min mem_top-13 /*smallest statically allocated word in + the one-word |mem|*/ +@d hi_mem_stat_usage 14 /*the number of one-word nodes always present*/ + +@ The following code gets |mem| off to a good start, when \TeX\ is +initializing itself the slow~way. + +@<Local variables for init...@>= +int @!k; /*index into |mem|, |eqtb|, etc.*/ + +@ @<Initialize table entries...@>= +for (k=mem_bot+1; k<=lo_mem_stat_max; k++) mem[k].sc=0; + /*all glue dimensions are zeroed*/ +@^data structure assumptions@> +k=mem_bot;@+while (k <= lo_mem_stat_max) + /*set first words of glue specifications*/ + {@+glue_ref_count(k)=null+1; + stretch_order(k)=normal;shrink_order(k)=normal; + k=k+glue_spec_size; + } +stretch(fil_glue)=unity;stretch_order(fil_glue)=fil;@/ +stretch(fill_glue)=unity;stretch_order(fill_glue)=fill;@/ +stretch(ss_glue)=unity;stretch_order(ss_glue)=fil;@/ +shrink(ss_glue)=unity;shrink_order(ss_glue)=fil;@/ +stretch(fil_neg_glue)=-unity;stretch_order(fil_neg_glue)=fil;@/ +rover=lo_mem_stat_max+1; +link(rover)=empty_flag; /*now initialize the dynamic memory*/ +node_size(rover)=1000; /*which is a 1000-word available node*/ +llink(rover)=rover;rlink(rover)=rover;@/ +lo_mem_max=rover+1000;link(lo_mem_max)=null;info(lo_mem_max)=null;@/ +for (k=hi_mem_stat_min; k<=mem_top; k++) + mem[k]=mem[lo_mem_max]; /*clear list heads*/ +@<Initialize the special list heads and constant nodes@>; +avail=null;mem_end=mem_top; +hi_mem_min=hi_mem_stat_min; /*initialize the one-word memory*/ +var_used=lo_mem_stat_max+1-mem_bot;dyn_used=hi_mem_stat_usage; + /*initialize statistics*/ + +@ If \TeX\ is extended improperly, the |mem| array might get screwed up. +For example, some pointers might be wrong, or some ``dead'' nodes might not +have been freed when the last reference to them disappeared. Procedures +|check_mem| and |search_mem| are available to help diagnose such +problems. These procedures make use of two arrays called |is_free| and +|was_free| that are present only if \TeX's debugging routines have +been included. (You may want to decrease the size of |mem| while you +@^debugging@> +are debugging.) + +@<Glob...@>= +#ifdef @!DEBUG +static bool @!is_free0[mem_max-mem_min+1], + *const @!is_free = @!is_free0-mem_min; /*free cells*/ +@t\hskip10pt@>static bool @!was_free0[mem_max-mem_min+1], + *const @!was_free = @!was_free0-mem_min; + /*previously free cells*/ +@t\hskip10pt@>static pointer @!was_mem_end, @!was_lo_max, @!was_hi_min; + /*previous |mem_end|, |lo_mem_max|, and |hi_mem_min|*/ +@t\hskip10pt@>static bool @!panicking; /*do we want to check memory constantly?*/ +#endif + +@ @<Set initial...@>= +#ifdef @!DEBUG +was_mem_end=mem_min; /*indicate that everything was previously free*/ +was_lo_max=mem_min;was_hi_min=mem_max; +panicking=false; +#endif + +@ 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 +#ifdef @!DEBUG +static void check_mem(bool @!print_locs) +{@+ /*loop exits*/ +int p, @!q; /*current locations of interest in |mem|*/ +bool @!clobbered; /*is something amiss?*/ +for (p=mem_min; p<=lo_mem_max; p++) is_free[p]=false; /*you can probably + do this faster*/ +for (p=hi_mem_min; p<=mem_end; p++) is_free[p]=false; /*ditto*/ +@<Check single-word |avail| list@>; +@<Check variable-size |avail| list@>; +@<Check flags of unavailable nodes@>; +if (print_locs) @<Print newly busy locations@>; +for (p=mem_min; p<=lo_mem_max; p++) was_free[p]=is_free[p]; +for (p=hi_mem_min; p<=mem_end; p++) was_free[p]=is_free[p]; + /*|was_free=is_free| might be faster*/ +was_mem_end=mem_end;was_lo_max=lo_mem_max;was_hi_min=hi_mem_min; +} +#endif + +@ @<Check single-word...@>= +p=avail;q=null;clobbered=false; +while (p!=null) + {@+if ((p > mem_end)||(p < hi_mem_min)) clobbered=true; + else if (is_free[p]) clobbered=true; + if (clobbered) + {@+print_nl("AVAIL list clobbered at "); +@.AVAIL list clobbered...@> + print_int(q);goto done1; + } + is_free[p]=true;q=p;p=link(q); + } +done1: + +@ @<Check variable-size...@>= +p=rover;q=null;clobbered=false; +@/do@+{if ((p >= lo_mem_max)||(p < mem_min)) clobbered=true; + else if ((rlink(p) >= lo_mem_max)||(rlink(p) < mem_min)) clobbered=true; + else if (!(is_empty(p))||(node_size(p) < 2)||@| + (p+node_size(p) > lo_mem_max)||@|(llink(rlink(p))!=p)) clobbered=true; + if (clobbered) + {@+print_nl("Double-AVAIL list clobbered at "); + print_int(q);goto done2; + } +for (q=p; q<=p+node_size(p)-1; q++) /*mark all locations free*/ + {@+if (is_free[q]) + {@+print_nl("Doubly free location at "); +@.Doubly free location...@> + print_int(q);goto done2; + } + is_free[q]=true; + } +q=p;p=rlink(p); +}@+ while (!(p==rover)); +done2: + +@ @<Check flags...@>= +p=mem_min; +while (p <= lo_mem_max) /*node |p| should not be empty*/ + {@+if (is_empty(p)) + {@+print_nl("Bad flag at ");print_int(p); +@.Bad flag...@> + } + while ((p <= lo_mem_max)&&!is_free[p]) incr(p); + while ((p <= lo_mem_max)&&is_free[p]) incr(p); + } + +@ @<Print newly busy...@>= +{@+print_nl("New busy locs:"); +for (p=mem_min; p<=lo_mem_max; p++) + if (!is_free[p]&&((p > was_lo_max)||was_free[p])) + {@+print_char(' ');print_int(p); + } +for (p=hi_mem_min; p<=mem_end; p++) + if (!is_free[p]&& + ((p < was_hi_min)||(p > was_mem_end)||was_free[p])) + {@+print_char(' ');print_int(p); + } +} + +@ 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 +#ifdef @!DEBUG +static void search_mem(pointer @!p) /*look for pointers to |p|*/ +{@+int q; /*current position being searched*/ +for (q=mem_min; q<=lo_mem_max; q++) + {@+if (link(q)==p) + {@+print_nl("LINK(");print_int(q);print_char(')'); + } + if (info(q)==p) + {@+print_nl("INFO(");print_int(q);print_char(')'); + } + } +for (q=hi_mem_min; q<=mem_end; q++) + {@+if (link(q)==p) + {@+print_nl("LINK(");print_int(q);print_char(')'); + } + if (info(q)==p) + {@+print_nl("INFO(");print_int(q);print_char(')'); + } + } +@<Search |eqtb| for equivalents equal to |p|@>; +@<Search |save_stack| for equivalents that point to |p|@>; +@<Search |hyph_list| for pointers to |p|@>; +} +#endif + +@* Displaying boxes. +We can reinforce our knowledge of the data structures just introduced +by considering two procedures that display a list in symbolic form. +The first of these, called |short_display|, is used in ``overfull box'' +messages to give the top-level description of a list. The other one, +called |show_node_list|, prints a detailed description of exactly what +is in the data structure. + +The philosophy of |short_display| is to ignore the fine points about exactly +what is inside boxes, except that ligatures and discretionary breaks are +expanded. As a result, |short_display| is a recursive procedure, but the +recursion is never more than one level deep. +@^recursion@> + +A global variable |font_in_short_display| keeps track of the font code that +is assumed to be present when |short_display| begins; deviations from this +font will be printed. + +@<Glob...@>= +static int @!font_in_short_display; /*an internal font number*/ + +@ Boxes, rules, inserts, whatsits, marks, and things in general that are +sort of ``complicated'' are indicated only by printing `\.{[]}'. + +@p static void short_display(int @!p) /*prints highlights of list |p|*/ +{@+int n; /*for replacement counts*/ +while (p > mem_min) + {@+if (is_char_node(p)) + {@+if (p <= mem_end) + {@+if (font(p)!=font_in_short_display) + {@+if ((font(p) < font_base)||(font(p) > font_max)) + print_char('*'); +@.*\relax@> + else@<Print the font identifier for |font(p)|@>; + print_char(' ');font_in_short_display=font(p); + } + print_ASCII(qo(character(p))); + } + } + else@<Print a short indication of the contents of node |p|@>; + p=link(p); + } +} + +@ @<Print a short indication of the contents of node |p|@>= +switch (type(p)) { +case hlist_node: case vlist_node: case ins_node: + case whatsit_node: case mark_node: case adjust_node: + case unset_node: print("[]");@+break; +case rule_node: print_char('|');@+break; +case glue_node: if (glue_ptr(p)!=zero_glue) print_char(' ');@+break; +case math_node: print_char('$');@+break; +case ligature_node: short_display(lig_ptr(p));@+break; +case disc_node: {@+short_display(pre_break(p)); + short_display(post_break(p));@/ + n=replace_count(p); + while (n > 0) + {@+if (link(p)!=null) p=link(p); + decr(n); + } + } @+break; +default:do_nothing; +} + +@ The |show_node_list| routine requires some auxiliary subroutines: one to +print a font-and-character combination, one to print a token list without +its reference count, and one to print a rule dimension. + +@p static void print_font_and_char(int @!p) /*prints |char_node| data*/ +{@+if (p > mem_end) print_esc("CLOBBERED."); +else{@+if ((font(p) < font_base)||(font(p) > font_max)) print_char('*'); +@.*\relax@> + else@<Print the font identifier for |font(p)|@>; + print_char(' ');print_ASCII(qo(character(p))); + } +} +@# +static void print_mark(int @!p) /*prints token list data in braces*/ +{@+print_char('{'); +if ((p < hi_mem_min)||(p > mem_end)) print_esc("CLOBBERED."); +else show_token_list(link(p), null, max_print_line-10); +print_char('}'); +} +@# +static void print_rule_dimen(scaled @!d) /*prints dimension in rule node*/ +{@+if (is_running(d)) print_char('*');else print_scaled(d); +@.*\relax@> +} + +@ Then there is a subroutine that prints glue stretch and shrink, possibly +followed by the name of finite units: + +@p static void print_glue(scaled @!d, int @!order, char *@!s) + /*prints a glue component*/ +{@+print_scaled(d); +if ((order < normal)||(order > filll)) print("foul"); +else if (order > normal) + {@+print("fil"); + while (order > fil) + {@+print_char('l');decr(order); + } + } +else if (s!=0) print(s); +} + +@ The next subroutine prints a whole glue specification. + +@p static void print_spec(int @!p, char *@!s) + /*prints a glue specification*/ +{@+if ((p < mem_min)||(p >= lo_mem_max)) print_char('*'); +@.*\relax@> +else{@+print_scaled(width(p)); + if (s!=0) print(s); + if (stretch(p)!=0) + {@+print(" plus ");print_glue(stretch(p), stretch_order(p), s); + } + if (shrink(p)!=0) + {@+print(" minus ");print_glue(shrink(p), shrink_order(p), s); + } + } +} + +@ We also need to declare some procedures that appear later in this +documentation. + +@p @<Declare procedures needed for displaying the elements of mlists@>@; +@<Declare the procedure called |print_skip_param|@>@; + +@ Since boxes can be inside of boxes, |show_node_list| is inherently recursive, +@^recursion@> +up to a given maximum number of levels. The history of nesting is indicated +by the current string, which will be printed at the beginning of each line; +the length of this string, namely |cur_length|, is the depth of nesting. + +Recursive calls on |show_node_list| therefore use the following pattern: + +@d node_list_display(A) + {@+append_char('.');show_node_list(A);flush_char; + } /*|str_room| need not be checked; see |show_box| below*/ + +@ A global variable called |depth_threshold| is used to record the maximum +depth of nesting for which |show_node_list| will show information. If we +have |depth_threshold==0|, for example, only the top level information will +be given and no sublists will be traversed. Another global variable, called +|breadth_max|, tells the maximum number of items to show at each level; +|breadth_max| had better be positive, or you won't see anything. + +@<Glob...@>= +static int @!depth_threshold; /*maximum nesting depth in box displays*/ +static int @!breadth_max; /*maximum number of items shown at the same list level*/ + +@ Now we are ready for |show_node_list| itself. This procedure has been +written to be ``extra robust'' in the sense that it should not crash or get +into a loop even if the data structures have been messed up by bugs in +the rest of the program. You can safely call its parent routine +|show_box(p)| for arbitrary values of |p| when you are debugging \TeX. +However, in the presence of bad data, the procedure may +@^dirty \PASCAL@>@^debugging@> +fetch a |memory_word| whose variant is different from the way it was stored; +for example, it might try to read |mem[p].hh| when |mem[p]| +contains a scaled integer, if |p| is a pointer that has been +clobbered or chosen at random. + +@p static void show_node_list(int @!p) /*prints a node list symbolically*/ +{@+ +int n; /*the number of items already printed at this level*/ +double @!g; /*a glue ratio, as a floating point number*/ +if (cur_length > depth_threshold) + {@+if (p > null) print(" []"); + /*indicate that there's been some truncation*/ + return; + } +n=0; +while (p > mem_min) + {@+print_ln();print_current_string(); /*display the nesting history*/ + if (p > mem_end) /*pointer out of range*/ + {@+print("Bad link, display aborted.");return; +@.Bad link...@> + } + incr(n);if (n > breadth_max) /*time to stop*/ + {@+print("etc.");return; +@.etc@> + } + @<Display node |p|@>; + p=link(p); + } + +} + +@ @<Display node |p|@>= +if (is_char_node(p)) print_font_and_char(p); +else switch (type(p)) { + case hlist_node: case vlist_node: case unset_node: @<Display box |p|@>@;@+break; + case rule_node: @<Display rule |p|@>@;@+break; + case ins_node: @<Display insertion |p|@>@;@+break; + case whatsit_node: @<Display the whatsit node |p|@>@;@+break; + case glue_node: @<Display glue |p|@>@;@+break; + case kern_node: @<Display kern |p|@>@;@+break; + case math_node: @<Display math node |p|@>@;@+break; + case ligature_node: @<Display ligature |p|@>@;@+break; + case penalty_node: @<Display penalty |p|@>@;@+break; + case disc_node: @<Display discretionary |p|@>@;@+break; + case mark_node: @<Display mark |p|@>@;@+break; + case adjust_node: @<Display adjustment |p|@>@;@+break; + @t\4@>@<Cases of |show_node_list| that arise in mlists only@>@; + default:print("Unknown node type!"); + } + +@ @<Display box |p|@>= +{@+if (type(p)==hlist_node) print_esc("h"); +else if (type(p)==vlist_node) print_esc("v"); +else print_esc("unset"); +print("box(");print_scaled(height(p));print_char('+'); +print_scaled(depth(p));print(")x");print_scaled(width(p)); +if (type(p)==unset_node) + @<Display special fields of the unset node |p|@>@; +else{@+@<Display the value of |glue_set(p)|@>; + if (shift_amount(p)!=0) + {@+print(", shifted ");print_scaled(shift_amount(p)); + } + } +node_list_display(list_ptr(p)); /*recursive call*/ +} + +@ @<Display special fields of the unset node |p|@>= +{@+if (span_count(p)!=min_quarterword) + {@+print(" (");print_int(qo(span_count(p))+1); + print(" columns)"); + } +if (glue_stretch(p)!=0) + {@+print(", stretch ");print_glue(glue_stretch(p), glue_order(p), 0); + } +if (glue_shrink(p)!=0) + {@+print(", shrink ");print_glue(glue_shrink(p), glue_sign(p), 0); + } +} + +@ The code will have to change in this place if |glue_ratio| is +a structured type instead of an ordinary |double|. Note that this routine +should avoid arithmetic errors even if the |glue_set| field holds an +arbitrary random value. The following code assumes that a properly +formed nonzero |double| number has absolute value $2^{20}$ or more when +it is regarded as an integer; this precaution was adequate to prevent +floating point underflow on the author's computer. +@^system dependencies@> +@^dirty \PASCAL@> + +@<Display the value of |glue_set(p)|@>= +g=unfix(glue_set(p)); +if ((g!=float_constant(0))&&(glue_sign(p)!=normal)) + {@+print(", glue set "); + if (glue_sign(p)==shrinking) print("- "); + if (abs(mem[p+glue_offset].i) < 04000000) print("?.?"); + else if (abs(g) > float_constant(20000)) + {@+if (g > float_constant(0)) print_char('>'); + else print("< -"); + print_glue(20000*unity, glue_order(p), 0); + } + else print_glue(round(unity*g), glue_order(p), 0); +@^real multiplication@> + } + +@ @<Display rule |p|@>= +{@+print_esc("rule(");print_rule_dimen(height(p));print_char('+'); +print_rule_dimen(depth(p));print(")x");print_rule_dimen(width(p)); +} + +@ @<Display insertion |p|@>= +{@+print_esc("insert");print_int(qo(subtype(p))); +print(", natural size ");print_scaled(height(p)); +print("; split(");print_spec(split_top_ptr(p), 0); +print_char(',');print_scaled(depth(p)); +print("); float cost ");print_int(float_cost(p)); +node_list_display(ins_ptr(p)); /*recursive call*/ +} + +@ @<Display glue |p|@>= +if (subtype(p) >= a_leaders) @<Display leaders |p|@>@; +else{@+print_esc("glue"); + if (subtype(p)!=normal) + {@+print_char('('); + if (subtype(p) < cond_math_glue) + print_skip_param(subtype(p)-1); + else if (subtype(p)==cond_math_glue) print_esc("nonscript"); + else print_esc("mskip"); + print_char(')'); + } + if (subtype(p)!=cond_math_glue) + {@+print_char(' '); + if (subtype(p) < cond_math_glue) print_spec(glue_ptr(p), 0); + else print_spec(glue_ptr(p),"mu"); + } + } + +@ @<Display leaders |p|@>= +{@+print_esc(""); +if (subtype(p)==c_leaders) print_char('c'); +else if (subtype(p)==x_leaders) print_char('x'); +print("leaders ");print_spec(glue_ptr(p), 0); +node_list_display(leader_ptr(p)); /*recursive call*/ +} + +@ An ``explicit'' kern value is indicated implicitly by an explicit space. + +@<Display kern |p|@>= +if (subtype(p)!=mu_glue) + {@+print_esc("kern"); + if (subtype(p)!=normal) print_char(' '); + print_scaled(width(p)); + if (subtype(p)==acc_kern) print(" (for accent)"); +@.for accent@> + } +else{@+print_esc("mkern");print_scaled(width(p));print("mu"); + } + +@ @<Display math node |p|@>= +{@+print_esc("math"); +if (subtype(p)==before) print("on"); +else print("off"); +if (width(p)!=0) + {@+print(", surrounded ");print_scaled(width(p)); + } +} + +@ @<Display ligature |p|@>= +{@+print_font_and_char(lig_char(p));print(" (ligature "); +if (subtype(p) > 1) print_char('|'); +font_in_short_display=font(lig_char(p));short_display(lig_ptr(p)); +if (odd(subtype(p))) print_char('|'); +print_char(')'); +} + +@ @<Display penalty |p|@>= +{@+print_esc("penalty ");print_int(penalty(p)); +} + +@ The |post_break| list of a discretionary node is indicated by a prefixed +`\.{\char'174}' instead of the `\..' before the |pre_break| list. + +@<Display discretionary |p|@>= +{@+print_esc("discretionary"); +if (replace_count(p) > 0) + {@+print(" replacing ");print_int(replace_count(p)); + } +node_list_display(pre_break(p)); /*recursive call*/ +append_char('|');show_node_list(post_break(p));flush_char; /*recursive call*/ +} + +@ @<Display mark |p|@>= +{@+print_esc("mark"); +if (mark_class(p)!=0) + {@+print_char('s');print_int(mark_class(p)); + } +print_mark(mark_ptr(p)); +} + +@ @<Display adjustment |p|@>= +{@+print_esc("vadjust");node_list_display(adjust_ptr(p)); /*recursive call*/ +} + +@ The recursive machinery is started by calling |show_box|. +@^recursion@> + +@p static void show_box(pointer @!p) +{@+@<Assign the values |depth_threshold:=show_box_depth| and |breadth_max:=show_box_breadth|@>; +if (breadth_max <= 0) breadth_max=5; +if (pool_ptr+depth_threshold >= pool_size) + depth_threshold=pool_size-pool_ptr-1; + /*now there's enough room for prefix string*/ +show_node_list(p); /*the show starts at |p|*/ +print_ln(); +} + +@* Destroying boxes. +When we are done with a node list, we are obliged to return it to free +storage, including all of its sublists. The recursive procedure +|flush_node_list| does this for us. + +@ First, however, we shall consider two non-recursive procedures that do +simpler tasks. The first of these, |delete_token_ref|, is called when +a pointer to a token list's reference count is being removed. This means +that the token list should disappear if the reference count was |null|, +otherwise the count should be decreased by one. +@^reference counts@> + +@d token_ref_count(A) info(A) /*reference count preceding a token list*/ + +@p static void delete_token_ref(pointer @!p) /*|p| points to the reference count + of a token list that is losing one reference*/ +{@+if (token_ref_count(p)==null) flush_list(p); +else decr(token_ref_count(p)); +} + +@ Similarly, |delete_glue_ref| is called when a pointer to a glue +specification is being withdrawn. +@^reference counts@> +@d fast_delete_glue_ref(A) @t@>@;@/ + {@+if (glue_ref_count(A)==null) free_node(A, glue_spec_size); + else decr(glue_ref_count(A)); + } + +@p static void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/ +fast_delete_glue_ref(p) + +@ Now we are ready to delete any node list, recursively. +In practice, the nodes deleted are usually charnodes (about 2/3 of the time), +and they are glue nodes in about half of the remaining cases. +@^recursion@> + +@p static void flush_node_list(pointer @!p) /*erase list of nodes starting at |p|*/ +{@+ /*go here when node |p| has been freed*/ +pointer q; /*successor to node |p|*/ +while (p!=null) +@^inner loop@> + {@+q=link(p); + if (is_char_node(p)) free_avail(p)@; + else{@+switch (type(p)) { + case hlist_node: case vlist_node: + case unset_node: {@+flush_node_list(list_ptr(p)); + free_node(p, box_node_size);goto done; + } + case rule_node: {@+free_node(p, rule_node_size);goto done; + } + case ins_node: {@+flush_node_list(ins_ptr(p)); + delete_glue_ref(split_top_ptr(p)); + free_node(p, ins_node_size);goto done; + } + case whatsit_node: @<Wipe out the whatsit node |p| and |goto done|@>@; + case glue_node: {@+fast_delete_glue_ref(glue_ptr(p)); + if (leader_ptr(p)!=null) flush_node_list(leader_ptr(p)); + } @+break; + case kern_node: case math_node: case penalty_node: do_nothing;@+break; + case ligature_node: flush_node_list(lig_ptr(p));@+break; + case mark_node: delete_token_ref(mark_ptr(p));@+break; + case disc_node: {@+flush_node_list(pre_break(p)); + flush_node_list(post_break(p)); + } @+break; + case adjust_node: flush_node_list(adjust_ptr(p));@+break; + @t\4@>@<Cases of |flush_node_list| that arise in mlists only@>@; + default:confusion("flushing"); +@:this can't happen flushing}{\quad flushing@> + } @/ + free_node(p, small_node_size); + done: ;} + p=q; + } +} + +@* Copying boxes. +Another recursive operation that acts on boxes is sometimes needed: The +procedure |copy_node_list| returns a pointer to another node list that has +the same structure and meaning as the original. Note that since glue +specifications and token lists have reference counts, we need not make +copies of them. Reference counts can never get too large to fit in a +halfword, since each pointer to a node is in a different memory address, +and the total number of memory addresses fits in a halfword. +@^recursion@> +@^reference counts@> + +(Well, there actually are also references from outside |mem|; if the +|save_stack| is made arbitrarily large, it would theoretically be possible +to break \TeX\ by overflowing a reference count. But who would want to do that?) + +@d add_token_ref(A) incr(token_ref_count(A)) /*new reference to a token list*/ +@d add_glue_ref(A) incr(glue_ref_count(A)) /*new reference to a glue spec*/ + +@ The copying procedure copies words en masse without bothering +to look at their individual fields. If the node format changes---for +example, if the size is altered, or if some link field is moved to another +relative position---then this code may need to be changed too. +@^data structure assumptions@> + +@p static pointer copy_node_list(pointer @!p) /*makes a duplicate of the + node list that starts at |p| and returns a pointer to the new list*/ +{@+pointer h; /*temporary head of copied list*/ +pointer @!q; /*previous position in new list*/ +pointer @!r; /*current node being fabricated for new list*/ +int @!words; /*number of words remaining to be copied*/ +h=get_avail();q=h; +while (p!=null) + {@+@<Make a copy of node |p| in node |r|@>; + link(q)=r;q=r;p=link(p); + } +link(q)=null;q=link(h);free_avail(h); +return q; +} + +@ @<Make a copy of node |p|...@>= +words=1; /*this setting occurs in more branches than any other*/ +if (is_char_node(p)) r=get_avail(); +else@<Case statement to copy different types and set |words| to the number +of initial words not yet copied@>; +while (words > 0) + {@+decr(words);mem[r+words]=mem[p+words]; + } + +@ @<Case statement to copy...@>= +switch (type(p)) { +case hlist_node: case vlist_node: case unset_node: {@+r=get_node(box_node_size); + mem[r+6]=mem[p+6];mem[r+5]=mem[p+5]; /*copy the last two words*/ + list_ptr(r)=copy_node_list(list_ptr(p)); /*this affects |mem[r+5]|*/ + words=5; + } @+break; +case rule_node: {@+r=get_node(rule_node_size);words=rule_node_size; + } @+break; +case ins_node: {@+r=get_node(ins_node_size);mem[r+4]=mem[p+4]; + add_glue_ref(split_top_ptr(p)); + ins_ptr(r)=copy_node_list(ins_ptr(p)); /*this affects |mem[r+4]|*/ + words=ins_node_size-1; + } @+break; +case whatsit_node: @<Make a partial copy of the whatsit node |p| and make +|r| point to it; set |words| to the number of initial words not yet copied@>@;@+break; +case glue_node: {@+r=get_node(small_node_size);add_glue_ref(glue_ptr(p)); + glue_ptr(r)=glue_ptr(p);leader_ptr(r)=copy_node_list(leader_ptr(p)); + } @+break; +case kern_node: case math_node: case penalty_node: {@+r=get_node(small_node_size); + words=small_node_size; + } @+break; +case ligature_node: {@+r=get_node(small_node_size); + mem[lig_char(r)]=mem[lig_char(p)]; /*copy |font| and |character|*/ + lig_ptr(r)=copy_node_list(lig_ptr(p)); + } @+break; +case disc_node: {@+r=get_node(small_node_size); + pre_break(r)=copy_node_list(pre_break(p)); + post_break(r)=copy_node_list(post_break(p)); + } @+break; +case mark_node: {@+r=get_node(small_node_size);add_token_ref(mark_ptr(p)); + words=small_node_size; + } @+break; +case adjust_node: {@+r=get_node(small_node_size); + adjust_ptr(r)=copy_node_list(adjust_ptr(p)); + } @+break; /*|words==1==small_node_size-1|*/ +default:confusion("copying"); +@:this can't happen copying}{\quad copying@> +} + +@* The command codes. +Before we can go any further, we need to define symbolic names for the internal +code numbers that represent the various commands obeyed by \TeX. These codes +are somewhat arbitrary, but not completely so. For example, the command +codes for character types are fixed by the language, since a user says, +e.g., `\.{\\catcode \`\\\${} = 3}' to make \.{\char'44} a math delimiter, +and the command code |math_shift| is equal to~3. Some other codes have +been made adjacent so that |case| statements in the program need not consider +cases that are widely spaced, or so that |case| statements can be replaced +by |if| statements. + +At any rate, here is the list, for future reference. First come the +``catcode'' commands, several of which share their numeric codes with +ordinary commands when the catcode cannot emerge from \TeX's scanning routine. + +@d escape 0 /*escape delimiter (called \.\\ in {\sl The \TeX book\/})*/ +@:TeXbook}{\sl The \TeX book@> +@d relax 0 /*do nothing ( \.{\\relax} )*/ +@d left_brace 1 /*beginning of a group ( \.\{ )*/ +@d right_brace 2 /*ending of a group ( \.\} )*/ +@d math_shift 3 /*mathematics shift character ( \.\$ )*/ +@d tab_mark 4 /*alignment delimiter ( \.\&, \.{\\span} )*/ +@d car_ret 5 /*end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )*/ +@d out_param 5 /*output a macro parameter*/ +@d mac_param 6 /*macro parameter symbol ( \.\# )*/ +@d sup_mark 7 /*superscript ( \.{\char'136} )*/ +@d sub_mark 8 /*subscript ( \.{\char'137} )*/ +@d ignore 9 /*characters to ignore ( \.{\^\^@@} )*/ +@d endv 9 /*end of \<v_j> list in alignment template*/ +@d spacer 10 /*characters equivalent to blank space ( \.{\ } )*/ +@d letter 11 /*characters regarded as letters ( \.{A..Z}, \.{a..z} )*/ +@d other_char 12 /*none of the special character types*/ +@d active_char 13 /*characters that invoke macros ( \.{\char`\~} )*/ +@d par_end 13 /*end of paragraph ( \.{\\par} )*/ +@d match 13 /*match a macro parameter*/ +@d comment 14 /*characters that introduce comments ( \.\% )*/ +@d end_match 14 /*end of parameters to macro*/ +@d stop 14 /*end of job ( \.{\\end}, \.{\\dump} )*/ +@d invalid_char 15 /*characters that shouldn't appear ( \.{\^\^?} )*/ +@d delim_num 15 /*specify delimiter numerically ( \.{\\delimiter} )*/ +@d max_char_code 15 /*largest catcode for individual characters*/ + +@ Next are the ordinary run-of-the-mill command codes. Codes that are +|min_internal| or more represent internal quantities that might be +expanded by `\.{\\the}'. + +@d char_num 16 /*character specified numerically ( \.{\\char} )*/ +@d math_char_num 17 /*explicit math code ( \.{\\mathchar} )*/ +@d mark 18 /*mark definition ( \.{\\mark} )*/ +@d xray 19 /*peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)*/ +@d make_box 20 /*make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)*/ +@d hmove 21 /*horizontal motion ( \.{\\moveleft}, \.{\\moveright} )*/ +@d vmove 22 /*vertical motion ( \.{\\raise}, \.{\\lower} )*/ +@d un_hbox 23 /*unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )*/ +@d un_vbox 24 /*unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )*/ + /*( or \.{\\pagediscards}, \.{\\splitdiscards} )*/ +@d remove_item 25 /*nullify last item ( \.{\\unpenalty}, + \.{\\unkern}, \.{\\unskip} )*/ +@d hskip 26 /*horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)*/ +@d vskip 27 /*vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)*/ +@d mskip 28 /*math glue ( \.{\\mskip} )*/ +@d kern 29 /*fixed space ( \.{\\kern} )*/ +@d mkern 30 /*math kern ( \.{\\mkern} )*/ +@d leader_ship 31 /*use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)*/ +@d halign 32 /*horizontal table alignment ( \.{\\halign} )*/ +@d valign 33 /*vertical table alignment ( \.{\\valign} )*/ +@d no_align 34 /*temporary escape from alignment ( \.{\\noalign} )*/ +@d vrule 35 /*vertical rule ( \.{\\vrule} )*/ +@d hrule 36 /*horizontal rule ( \.{\\hrule} )*/ +@d insert 37 /*vlist inserted in box ( \.{\\insert} )*/ +@d vadjust 38 /*vlist inserted in enclosing paragraph ( \.{\\vadjust} )*/ +@d ignore_spaces 39 /*gobble |spacer| tokens ( \.{\\ignorespaces} )*/ +@d after_assignment 40 /*save till assignment is done ( \.{\\afterassignment} )*/ +@d after_group 41 /*save till group is done ( \.{\\aftergroup} )*/ +@d break_penalty 42 /*additional badness ( \.{\\penalty} )*/ +@d start_par 43 /*begin paragraph ( \.{\\indent}, \.{\\noindent} )*/ +@d ital_corr 44 /*italic correction ( \.{\\/} )*/ +@d accent 45 /*attach accent in text ( \.{\\accent} )*/ +@d math_accent 46 /*attach accent in math ( \.{\\mathaccent} )*/ +@d discretionary 47 /*discretionary texts ( \.{\\-}, \.{\\discretionary} )*/ +@d eq_no 48 /*equation number ( \.{\\eqno}, \.{\\leqno} )*/ +@d left_right 49 /*variable delimiter ( \.{\\left}, \.{\\right} )*/ + /*( or \.{\\middle} )*/ +@d math_comp 50 /*component of formula ( \.{\\mathbin}, etc.~)*/ +@d limit_switch 51 /*diddle limit conventions ( \.{\\displaylimits}, etc.~)*/ +@d above 52 /*generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)*/ +@d math_style 53 /*style specification ( \.{\\displaystyle}, etc.~)*/ +@d math_choice 54 /*choice specification ( \.{\\mathchoice} )*/ +@d non_script 55 /*conditional math glue ( \.{\\nonscript} )*/ +@d vcenter 56 /*vertically center a vbox ( \.{\\vcenter} )*/ +@d case_shift 57 /*force specific case ( \.{\\lowercase}, \.{\\uppercase}~)*/ +@d message 58 /*send to user ( \.{\\message}, \.{\\errmessage} )*/ +@d extension 59 /*extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)*/ +@d in_stream 60 /*files for reading ( \.{\\openin}, \.{\\closein} )*/ +@d begin_group 61 /*begin local grouping ( \.{\\begingroup} )*/ +@d end_group 62 /*end local grouping ( \.{\\endgroup} )*/ +@d omit 63 /*omit alignment template ( \.{\\omit} )*/ +@d ex_space 64 /*explicit space ( \.{\\\ } )*/ +@d no_boundary 65 /*suppress boundary ligatures ( \.{\\noboundary} )*/ +@d radical 66 /*square root and similar signs ( \.{\\radical} )*/ +@d end_cs_name 67 /*end control sequence ( \.{\\endcsname} )*/ +@d min_internal 68 /*the smallest code that can follow \.{\\the}*/ +@d char_given 68 /*character code defined by \.{\\chardef}*/ +@d math_given 69 /*math code defined by \.{\\mathchardef}*/ +@d last_item 70 /*most recent item ( \.{\\lastpenalty}, + \.{\\lastkern}, \.{\\lastskip} )*/ +@d max_non_prefixed_command 70 /*largest command code that can't be \.{\\global}*/ + +@ The next codes are special; they all relate to mode-independent +assignment of values to \TeX's internal registers or tables. +Codes that are |max_internal| or less represent internal quantities +that might be expanded by `\.{\\the}'. + +@d toks_register 71 /*token list register ( \.{\\toks} )*/ +@d assign_toks 72 /*special token list ( \.{\\output}, \.{\\everypar}, etc.~)*/ +@d assign_int 73 /*user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)*/ +@d assign_dimen 74 /*user-defined length ( \.{\\hsize}, etc.~)*/ +@d assign_glue 75 /*user-defined glue ( \.{\\baselineskip}, etc.~)*/ +@d assign_mu_glue 76 /*user-defined muglue ( \.{\\thinmuskip}, etc.~)*/ +@d assign_font_dimen 77 /*user-defined font dimension ( \.{\\fontdimen} )*/ +@d assign_font_int 78 /*user-defined font integer ( \.{\\hyphenchar}, + \.{\\skewchar} )*/ +@d set_aux 79 /*specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )*/ +@d set_prev_graf 80 /*specify state info ( \.{\\prevgraf} )*/ +@d set_page_dimen 81 /*specify state info ( \.{\\pagegoal}, etc.~)*/ +@d set_page_int 82 /*specify state info ( \.{\\deadcycles}, + \.{\\insertpenalties} )*/ + /*( or \.{\\interactionmode} )*/ +@d set_box_dimen 83 /*change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )*/ +@d set_shape 84 /*specify fancy paragraph shape ( \.{\\parshape} )*/ + /*(or \.{\\interlinepenalties}, etc.~)*/ +@d def_code 85 /*define a character code ( \.{\\catcode}, etc.~)*/ +@d def_family 86 /*declare math fonts ( \.{\\textfont}, etc.~)*/ +@d set_font 87 /*set current font ( font identifiers )*/ +@d def_font 88 /*define a font file ( \.{\\font} )*/ +@d internal_register 89 /*internal register ( \.{\\count}, \.{\\dimen}, etc.~)*/ +@d max_internal 89 /*the largest code that can follow \.{\\the}*/ +@d advance 90 /*advance a register or parameter ( \.{\\advance} )*/ +@d multiply 91 /*multiply a register or parameter ( \.{\\multiply} )*/ +@d divide 92 /*divide a register or parameter ( \.{\\divide} )*/ +@d prefix 93 /*qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} )*/ + /*( or \.{\\protected} )*/ +@d let 94 /*assign a command code ( \.{\\let}, \.{\\futurelet} )*/ +@d shorthand_def 95 /*code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)*/ +@d read_to_cs 96 /*read into a control sequence ( \.{\\read} )*/ + /*( or \.{\\readline} )*/ +@d def 97 /*macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )*/ +@d set_box 98 /*set a box ( \.{\\setbox} )*/ +@d hyph_data 99 /*hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )*/ +@d set_interaction 100 /*define level of interaction ( \.{\\batchmode}, etc.~)*/ +@d max_command 100 /*the largest command code seen at |big_switch|*/ + +@ The remaining command codes are extra special, since they cannot get through +\TeX's scanner to the main control routine. They have been given values higher +than |max_command| so that their special nature is easily discernible. +The ``expandable'' commands come first. + +@d undefined_cs (max_command+1) /*initial state of most |eq_type| fields*/ +@d expand_after (max_command+2) /*special expansion ( \.{\\expandafter} )*/ +@d no_expand (max_command+3) /*special nonexpansion ( \.{\\noexpand} )*/ +@d input (max_command+4) /*input a source file ( \.{\\input}, \.{\\endinput} )*/ + /*( or \.{\\scantokens} )*/ +@d if_test (max_command+5) /*conditional text ( \.{\\if}, \.{\\ifcase}, etc.~)*/ +@d fi_or_else (max_command+6) /*delimiters for conditionals ( \.{\\else}, etc.~)*/ +@d cs_name (max_command+7) /*make a control sequence from tokens ( \.{\\csname} )*/ +@d convert (max_command+8) /*convert to text ( \.{\\number}, \.{\\string}, etc.~)*/ +@d the (max_command+9) /*expand an internal quantity ( \.{\\the} )*/ + /*( or \.{\\unexpanded}, \.{\\detokenize} )*/ +@d top_bot_mark (max_command+10) /*inserted mark ( \.{\\topmark}, etc.~)*/ +@d call (max_command+11) /*non-long, non-outer control sequence*/ +@d long_call (max_command+12) /*long, non-outer control sequence*/ +@d outer_call (max_command+13) /*non-long, outer control sequence*/ +@d long_outer_call (max_command+14) /*long, outer control sequence*/ +@d end_template (max_command+15) /*end of an alignment template*/ +@d dont_expand (max_command+16) /*the following token was marked by \.{\\noexpand}*/ +@d glue_ref (max_command+17) /*the equivalent points to a glue specification*/ +@d shape_ref (max_command+18) /*the equivalent points to a parshape specification*/ +@d box_ref (max_command+19) /*the equivalent points to a box node, or is |null|*/ +@d data (max_command+20) /*the equivalent is simply a halfword number*/ + +@* The semantic nest. +\TeX\ is typically in the midst of building many lists at once. For example, +when a math formula is being processed, \TeX\ is in math mode and +working on an mlist; this formula has temporarily interrupted \TeX\ from +being in horizontal mode and building the hlist of a paragraph; and this +paragraph has temporarily interrupted \TeX\ from being in vertical mode +and building the vlist for the next page of a document. Similarly, when a +\.{\\vbox} occurs inside of an \.{\\hbox}, \TeX\ is temporarily +interrupted from working in restricted horizontal mode, and it enters +internal vertical mode. The ``semantic nest'' is a stack that +keeps track of what lists and modes are currently suspended. + +At each level of processing we are in one of six modes: + +\yskip\hang|vmode| stands for vertical mode (the page builder); + +\hang|hmode| stands for horizontal mode (the paragraph builder); + +\hang|mmode| stands for displayed formula mode; + +\hang|-vmode| stands for internal vertical mode (e.g., in a \.{\\vbox}); + +\hang|-hmode| stands for restricted horizontal mode (e.g., in an \.{\\hbox}); + +\hang|-mmode| stands for math formula mode (not displayed). + +\yskip\noindent The mode is temporarily set to zero while processing \.{\\write} +texts. + +Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that +\TeX's ``big semantic switch'' can select the appropriate thing to +do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current +mode and |cur_cmd| is the current command code. + +@d vmode 1 /*vertical mode*/ +@d hmode (vmode+max_command+1) /*horizontal mode*/ +@d mmode (hmode+max_command+1) /*math mode*/ + +@p static void print_mode(int @!m) /*prints the mode represented by |m|*/ +{@+if (m > 0) + switch (m/(max_command+1)) { + case 0: print("vertical");@+break; + case 1: print("horizontal");@+break; + case 2: print("display math"); + } +else if (m==0) print("no"); +else switch ((-m)/(max_command+1)) { + case 0: print("internal vertical");@+break; + case 1: print("restricted horizontal");@+break; + case 2: print("math"); + } +print(" mode"); +} + +@ The state of affairs at any semantic level can be represented by +five values: + +\yskip\hang|mode| is the number representing the semantic mode, as +just explained. + +\yskip\hang|head| is a |pointer| to a list head for the list being built; +|link(head)| therefore points to the first element of the list, or +to |null| if the list is empty. + +\yskip\hang|tail| is a |pointer| to the final node of the list being +built; thus, |tail==head| if and only if the list is empty. + +\yskip\hang|prev_graf| is the number of lines of the current paragraph that +have already been put into the present vertical list. + +\yskip\hang|aux| is an auxiliary |memory_word| that gives further information +that is needed to characterize the situation. + +\yskip\noindent +In vertical mode, |aux| is also known as |prev_depth|; it is the scaled +value representing the depth of the previous box, for use in baseline +calculations, or it is | <= -1000|pt if the next box on the vertical list is to +be exempt from baseline calculations. In horizontal mode, |aux| is also +known as |space_factor| and |clang|; it holds the current space factor used in +spacing calculations, and the current language used for hyphenation. +(The value of |clang| is undefined in restricted horizontal mode.) +In math mode, |aux| is also known as |incompleat_noad|; if +not |null|, it points to a record that represents the numerator of a +generalized fraction for which the denominator is currently being formed +in the current list. + +There is also a sixth quantity, |mode_line|, which correlates +the semantic nest with the user's input; |mode_line| contains the source +line number at which the current level of nesting was entered. The negative +of this line number is the |mode_line| at the level of the +user's output routine. + +A seventh quantity, |eTeX_aux|, is used by the extended features \eTeX. +In vertical modes it is known as |LR_save| and holds the LR stack when a +paragraph is interrupted by a displayed formula. In display math mode +it is known as |LR_box| and holds a pointer to a prototype box for the +display. In math mode it is known as |delim_ptr| and points to the most +recent |left_noad| or |middle_noad| of a |math_left_group|. + +In horizontal mode, the |prev_graf| field is used for initial language data. + +The semantic nest is an array called |nest| that holds the |mode|, |head|, +|tail|, |prev_graf|, |aux|, and |mode_line| values for all semantic levels +below the currently active one. Information about the currently active +level is kept in the global quantities |mode|, |head|, |tail|, |prev_graf|, +|aux|, and |mode_line|, which live in a \PASCAL\ record that is ready to +be pushed onto |nest| if necessary. + +@d ignore_depth -65536000 /*|prev_depth| value that is ignored*/ + +@<Types...@>= +typedef struct { int16_t @!mode_field;@+ + pointer @!head_field, @!tail_field; + pointer @!eTeX_aux_field; + int @!pg_field, @!ml_field;@+ + memory_word @!aux_field; + } list_state_record; + +@ @d mode cur_list.mode_field /*current mode*/ +@d head cur_list.head_field /*header node of current list*/ +@d tail cur_list.tail_field /*final node on current list*/ +@d eTeX_aux cur_list.eTeX_aux_field /*auxiliary data for \eTeX*/ +@d LR_save eTeX_aux /*LR stack when a paragraph is interrupted*/ +@d LR_box eTeX_aux /*prototype box for display*/ +@d delim_ptr eTeX_aux /*most recent left or right noad of a math left group*/ +@d prev_graf cur_list.pg_field /*number of paragraph lines accumulated*/ +@d aux cur_list.aux_field /*auxiliary data about the current list*/ +@d prev_depth aux.sc /*the name of |aux| in vertical mode*/ +@d space_factor aux.hh.lh /*part of |aux| in horizontal mode*/ +@d clang aux.hh.rh /*the other part of |aux| in horizontal mode*/ +@d incompleat_noad aux.i /*the name of |aux| in math mode*/ +@d mode_line cur_list.ml_field /*source file line number at beginning of list*/ + +@<Glob...@>= +static list_state_record @!nest[nest_size+1]; +static int @!nest_ptr; /*first unused location of |nest|*/ +static int @!max_nest_stack; /*maximum of |nest_ptr| when pushing*/ +static list_state_record @!cur_list; /*the ``top'' semantic state*/ +static int @!shown_mode; /*most recent mode shown by \.{\\tracingcommands}*/ + +@ Here is a common way to make the current list grow: + +@d tail_append(A) {@+link(tail)=A;tail=link(tail); + } + +@ We will see later that the vertical list at the bottom semantic level is split +into two parts; the ``current page'' runs from |page_head| to |page_tail|, +and the ``contribution list'' runs from |contrib_head| to |tail| of +semantic level zero. The idea is that contributions are first formed in +vertical mode, then ``contributed'' to the current page (during which time +the page-breaking decisions are made). For now, we don't need to know +any more details about the page-building process. + +@<Set init...@>= +nest_ptr=0;max_nest_stack=0; +mode=vmode;head=contrib_head;tail=contrib_head; +eTeX_aux=null; +prev_depth=ignore_depth;mode_line=0; +prev_graf=0;shown_mode=0; +@<Start a new current page@>; + +@ When \TeX's work on one level is interrupted, the state is saved by +calling |push_nest|. This routine changes |head| and |tail| so that +a new (empty) list is begun; it does not change |mode| or |aux|. + +@p static void push_nest(void) /*enter a new semantic level, save the old*/ +{@+if (nest_ptr > max_nest_stack) + {@+max_nest_stack=nest_ptr; + if (nest_ptr==nest_size) overflow("semantic nest size", nest_size); +@:TeX capacity exceeded semantic nest size}{\quad semantic nest size@> + } +nest[nest_ptr]=cur_list; /*stack the record*/ +incr(nest_ptr);head=get_avail();tail=head;prev_graf=0;mode_line=line; +eTeX_aux=null; +} + +@ Conversely, when \TeX\ is finished on the current level, the former +state is restored by calling |pop_nest|. This routine will never be +called at the lowest semantic level, nor will it be called unless |head| +is a node that should be returned to free memory. + +@p static void pop_nest(void) /*leave a semantic level, re-enter the old*/ +{@+free_avail(head);decr(nest_ptr);cur_list=nest[nest_ptr]; +} + +@ Here is a procedure that displays what \TeX\ is working on, at all levels. + +@p static void print_totals(void); +static void show_activities(void) +{@+int p; /*index into |nest|*/ +int @!m; /*mode*/ +memory_word @!a; /*auxiliary*/ +pointer @!q, @!r; /*for showing the current page*/ +int @!t; /*ditto*/ +nest[nest_ptr]=cur_list; /*put the top level into the array*/ +print_nl("");print_ln(); +for (p=nest_ptr; p>=0; p--) + {@+m=nest[p].mode_field;a=nest[p].aux_field; + print_nl("### ");print_mode(m); + print(" entered at line ");print_int(abs(nest[p].ml_field)); + if (m==hmode) if (nest[p].pg_field!=040600000) + {@+print(" (language");print_int(nest[p].pg_field%0200000); + print(":hyphenmin");print_int(nest[p].pg_field/020000000); + print_char(',');print_int((nest[p].pg_field/0200000)%0100); + print_char(')'); + } + if (nest[p].ml_field < 0) print(" (\\output routine)"); + if (p==0) + {@+@<Show the status of the current page@>; + if (link(contrib_head)!=null) + print_nl("### recent contributions:"); + } + show_box(link(nest[p].head_field)); + @<Show the auxiliary field, |a|@>; + } +} + +@ @<Show the auxiliary...@>= +switch (abs(m)/(max_command+1)) { +case 0: {@+print_nl("prevdepth "); + if (a.sc <= ignore_depth) print("ignored"); + else print_scaled(a.sc); + if (nest[p].pg_field!=0) + {@+print(", prevgraf "); + print_int(nest[p].pg_field);print(" line"); + if (nest[p].pg_field!=1) print_char('s'); + } + } @+break; +case 1: {@+print_nl("spacefactor ");print_int(a.hh.lh); + if (m > 0) @+if (a.hh.rh > 0) + {@+print(", current language ");print_int(a.hh.rh);@+ + } + } @+break; +case 2: if (a.i!=null) + {@+print("this will begin denominator of:");show_box(a.i);@+ + } +} /*there are no other cases*/ + +@* The table of equivalents. +Now that we have studied the data structures for \TeX's semantic routines, +we ought to consider the data structures used by its syntactic routines. In +other words, our next concern will be +the tables that \TeX\ looks at when it is scanning +what the user has written. + +The biggest and most important such table is called |eqtb|. It holds the +current ``equivalents'' of things; i.e., it explains what things mean +or what their current values are, for all quantities that are subject to +the nesting structure provided by \TeX's grouping mechanism. There are six +parts to |eqtb|: + +\yskip\hangg 1) |eqtb[active_base dotdot(hash_base-1)]| holds the current +equivalents of single-character control sequences. + +\yskip\hangg 2) |eqtb[hash_base dotdot(glue_base-1)]| holds the current +equivalents of multiletter control sequences. + +\yskip\hangg 3) |eqtb[glue_base dotdot(local_base-1)]| holds the current +equivalents of glue parameters like the current baselineskip. + +\yskip\hangg 4) |eqtb[local_base dotdot(int_base-1)]| holds the current +equivalents of local halfword quantities like the current box registers, +the current ``catcodes,'' the current font, and a pointer to the current +paragraph shape. + +\yskip\hangg 5) |eqtb[int_base dotdot(dimen_base-1)]| holds the current +equivalents of fullword integer parameters like the current hyphenation +penalty. + +\yskip\hangg 6) |eqtb[dimen_base dotdot eqtb_size]| holds the current equivalents +of fullword dimension parameters like the current hsize or amount of +hanging indentation. + +\yskip\noindent Note that, for example, the current amount of +baselineskip glue is determined by the setting of a particular location +in region~3 of |eqtb|, while the current meaning of the control sequence +`\.{\\baselineskip}' (which might have been changed by \.{\\def} or +\.{\\let}) appears in region~2. + +@ Each entry in |eqtb| is a |memory_word|. Most of these words are of type +|two_halves|, and subdivided into three fields: + +\yskip\hangg 1) The |eq_level| (a quarterword) is the level of grouping at +which this equivalent was defined. If the level is |level_zero|, the +equivalent has never been defined; |level_one| refers to the outer level +(outside of all groups), and this level is also used for global +definitions that never go away. Higher levels are for equivalents that +will disappear at the end of their group. @^global definitions@> + +\yskip\hangg 2) The |eq_type| (another quarterword) specifies what kind of +entry this is. There are many types, since each \TeX\ primitive like +\.{\\hbox}, \.{\\def}, etc., has its own special code. The list of +command codes above includes all possible settings of the |eq_type| field. + +\yskip\hangg 3) The |equiv| (a halfword) is the current equivalent value. +This may be a font number, a pointer into |mem|, or a variety of other +things. + +@d eq_level_field(A) A.hh.b1 +@d eq_type_field(A) A.hh.b0 +@d equiv_field(A) A.hh.rh +@d eq_level(A) eq_level_field(eqtb[A]) /*level of definition*/ +@d eq_type(A) eq_type_field(eqtb[A]) /*command code for equivalent*/ +@d equiv(A) equiv_field(eqtb[A]) /*equivalent value*/ +@d level_zero min_quarterword /*level for undefined quantities*/ +@d level_one (level_zero+1) /*outermost level for defined quantities*/ + +@ Many locations in |eqtb| have symbolic names. The purpose of the next +paragraphs is to define these names, and to set up the initial values of the +equivalents. + +In the first region we have 256 equivalents for ``active characters'' that +act as control sequences, followed by 256 equivalents for single-character +control sequences. + +Then comes region~2, which corresponds to the hash table that we will +define later. The maximum address in this region is used for a dummy +control sequence that is perpetually undefined. There also are several +locations for control sequences that are perpetually defined +(since they are used in error recovery). + +@d active_base 1 /*beginning of region 1, for active character equivalents*/ +@d single_base (active_base+256) /*equivalents of one-character control sequences*/ +@d null_cs (single_base+256) /*equivalent of \.{\\csname\\endcsname}*/ +@d hash_base (null_cs+1) /*beginning of region 2, for the hash table*/ +@d frozen_control_sequence (hash_base+hash_size) /*for error recovery*/ +@d frozen_protection frozen_control_sequence /*inaccessible but definable*/ +@d frozen_cr (frozen_control_sequence+1) /*permanent `\.{\\cr}'*/ +@d frozen_end_group (frozen_control_sequence+2) /*permanent `\.{\\endgroup}'*/ +@d frozen_right (frozen_control_sequence+3) /*permanent `\.{\\right}'*/ +@d frozen_fi (frozen_control_sequence+4) /*permanent `\.{\\fi}'*/ +@d frozen_end_template (frozen_control_sequence+5) /*permanent `\.{\\endtemplate}'*/ +@d frozen_endv (frozen_control_sequence+6) /*second permanent `\.{\\endtemplate}'*/ +@d frozen_relax (frozen_control_sequence+7) /*permanent `\.{\\relax}'*/ +@d end_write (frozen_control_sequence+8) /*permanent `\.{\\endwrite}'*/ +@d frozen_dont_expand (frozen_control_sequence+9) + /*permanent `\.{\\notexpanded:}'*/ +@d frozen_primitive (frozen_control_sequence+10) + /*permanent `\.{\\primitive:}'*/ +@d frozen_null_font (frozen_control_sequence+11) + /*permanent `\.{\\nullfont}'*/ +@d font_id_base (frozen_null_font-font_base) + /*begins table of 257 permanent font identifiers*/ +@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/ +@d glue_base (undefined_control_sequence+1) /*beginning of region 3*/ + +@<Initialize table entries...@>= +eq_type(undefined_control_sequence)=undefined_cs; +equiv(undefined_control_sequence)=null; +eq_level(undefined_control_sequence)=level_zero; +for (k=active_base; k<=undefined_control_sequence-1; k++) + eqtb[k]=eqtb[undefined_control_sequence]; + +@ Here is a routine that displays the current meaning of an |eqtb| entry +in region 1 or~2. (Similar routines for the other regions will appear +below.) + +@<Show equivalent |n|, in region 1 or 2@>= +{@+sprint_cs(n);print_char('=');print_cmd_chr(eq_type(n), equiv(n)); +if (eq_type(n) >= call) + {@+print_char(':');show_token_list(link(equiv(n)), null, 32); + } +} + +@ Region 3 of |eqtb| contains the 256 \.{\\skip} registers, as well as the +glue parameters defined here. It is important that the ``muskip'' +parameters have larger numbers than the others. + +@d line_skip_code 0 /*interline glue if |baseline_skip| is infeasible*/ +@d baseline_skip_code 1 /*desired glue between baselines*/ +@d par_skip_code 2 /*extra glue just above a paragraph*/ +@d above_display_skip_code 3 /*extra glue just above displayed math*/ +@d below_display_skip_code 4 /*extra glue just below displayed math*/ +@d above_display_short_skip_code 5 + /*glue above displayed math following short lines*/ +@d below_display_short_skip_code 6 + /*glue below displayed math following short lines*/ +@d left_skip_code 7 /*glue at left of justified lines*/ +@d right_skip_code 8 /*glue at right of justified lines*/ +@d top_skip_code 9 /*glue at top of main pages*/ +@d split_top_skip_code 10 /*glue at top of split pages*/ +@d tab_skip_code 11 /*glue between aligned entries*/ +@d space_skip_code 12 /*glue between words (if not |zero_glue|)*/ +@d xspace_skip_code 13 /*glue after sentences (if not |zero_glue|)*/ +@d par_fill_skip_code 14 /*glue on last line of paragraph*/ +@d thin_mu_skip_code 15 /*thin space in math formula*/ +@d med_mu_skip_code 16 /*medium space in math formula*/ +@d thick_mu_skip_code 17 /*thick space in math formula*/ +@d glue_pars 18 /*total number of glue parameters*/ +@d skip_base (glue_base+glue_pars) /*table of 256 ``skip'' registers*/ +@d mu_skip_base (skip_base+256) /*table of 256 ``muskip'' registers*/ +@d local_base (mu_skip_base+256) /*beginning of region 4*/ +@# +@d skip(A) equiv(skip_base+A) /*|mem| location of glue specification*/ +@d mu_skip(A) equiv(mu_skip_base+A) /*|mem| location of math glue spec*/ +@d glue_par(A) equiv(glue_base+A) /*|mem| location of glue specification*/ +@d line_skip glue_par(line_skip_code) +@d baseline_skip glue_par(baseline_skip_code) +@d par_skip glue_par(par_skip_code) +@d above_display_skip glue_par(above_display_skip_code) +@d below_display_skip glue_par(below_display_skip_code) +@d above_display_short_skip glue_par(above_display_short_skip_code) +@d below_display_short_skip glue_par(below_display_short_skip_code) +@d left_skip glue_par(left_skip_code) +@d right_skip glue_par(right_skip_code) +@d top_skip glue_par(top_skip_code) +@d split_top_skip glue_par(split_top_skip_code) +@d tab_skip glue_par(tab_skip_code) +@d space_skip glue_par(space_skip_code) +@d xspace_skip glue_par(xspace_skip_code) +@d par_fill_skip glue_par(par_fill_skip_code) +@d thin_mu_skip glue_par(thin_mu_skip_code) +@d med_mu_skip glue_par(med_mu_skip_code) +@d thick_mu_skip glue_par(thick_mu_skip_code) + +@<Current |mem| equivalent of glue parameter number |n|@>=glue_par(n) + +@ Sometimes we need to convert \TeX's internal code numbers into symbolic +form. The |print_skip_param| routine gives the symbolic name of a glue +parameter. + +@<Declare the procedure called |print_skip_param|@>= +static void print_skip_param(int @!n) +{@+switch (n) { +case line_skip_code: print_esc("lineskip");@+break; +case baseline_skip_code: print_esc("baselineskip");@+break; +case par_skip_code: print_esc("parskip");@+break; +case above_display_skip_code: print_esc("abovedisplayskip");@+break; +case below_display_skip_code: print_esc("belowdisplayskip");@+break; +case above_display_short_skip_code: print_esc("abovedisplayshortskip");@+break; +case below_display_short_skip_code: print_esc("belowdisplayshortskip");@+break; +case left_skip_code: print_esc("leftskip");@+break; +case right_skip_code: print_esc("rightskip");@+break; +case top_skip_code: print_esc("topskip");@+break; +case split_top_skip_code: print_esc("splittopskip");@+break; +case tab_skip_code: print_esc("tabskip");@+break; +case space_skip_code: print_esc("spaceskip");@+break; +case xspace_skip_code: print_esc("xspaceskip");@+break; +case par_fill_skip_code: print_esc("parfillskip");@+break; +case thin_mu_skip_code: print_esc("thinmuskip");@+break; +case med_mu_skip_code: print_esc("medmuskip");@+break; +case thick_mu_skip_code: print_esc("thickmuskip");@+break; +default:print("[unknown glue parameter!]"); +} +} + +@ The symbolic names for glue parameters are put into \TeX's hash table +by using the routine called |primitive|, defined below. Let us enter them +now, so that we don't have to list all those parameter names anywhere else. + +@<Put each of \TeX's primitives into the hash table@>= +primitive("lineskip", assign_glue, glue_base+line_skip_code);@/ +@!@:line\_skip\_}{\.{\\lineskip} primitive@> +primitive("baselineskip", assign_glue, glue_base+baseline_skip_code);@/ +@!@:baseline\_skip\_}{\.{\\baselineskip} primitive@> +primitive("parskip", assign_glue, glue_base+par_skip_code);@/ +@!@:par\_skip\_}{\.{\\parskip} primitive@> +primitive("abovedisplayskip", assign_glue, glue_base+above_display_skip_code);@/ +@!@:above\_display\_skip\_}{\.{\\abovedisplayskip} primitive@> +primitive("belowdisplayskip", assign_glue, glue_base+below_display_skip_code);@/ +@!@:below\_display\_skip\_}{\.{\\belowdisplayskip} primitive@> +primitive("abovedisplayshortskip", + assign_glue, glue_base+above_display_short_skip_code);@/ +@!@:above\_display\_short\_skip\_}{\.{\\abovedisplayshortskip} primitive@> +primitive("belowdisplayshortskip", + assign_glue, glue_base+below_display_short_skip_code);@/ +@!@:below\_display\_short\_skip\_}{\.{\\belowdisplayshortskip} primitive@> +primitive("leftskip", assign_glue, glue_base+left_skip_code);@/ +@!@:left\_skip\_}{\.{\\leftskip} primitive@> +primitive("rightskip", assign_glue, glue_base+right_skip_code);@/ +@!@:right\_skip\_}{\.{\\rightskip} primitive@> +primitive("topskip", assign_glue, glue_base+top_skip_code);@/ +@!@:top\_skip\_}{\.{\\topskip} primitive@> +primitive("splittopskip", assign_glue, glue_base+split_top_skip_code);@/ +@!@:split\_top\_skip\_}{\.{\\splittopskip} primitive@> +primitive("tabskip", assign_glue, glue_base+tab_skip_code);@/ +@!@:tab\_skip\_}{\.{\\tabskip} primitive@> +primitive("spaceskip", assign_glue, glue_base+space_skip_code);@/ +@!@:space\_skip\_}{\.{\\spaceskip} primitive@> +primitive("xspaceskip", assign_glue, glue_base+xspace_skip_code);@/ +@!@:xspace\_skip\_}{\.{\\xspaceskip} primitive@> +primitive("parfillskip", assign_glue, glue_base+par_fill_skip_code);@/ +@!@:par\_fill\_skip\_}{\.{\\parfillskip} primitive@> +primitive("thinmuskip", assign_mu_glue, glue_base+thin_mu_skip_code);@/ +@!@:thin\_mu\_skip\_}{\.{\\thinmuskip} primitive@> +primitive("medmuskip", assign_mu_glue, glue_base+med_mu_skip_code);@/ +@!@:med\_mu\_skip\_}{\.{\\medmuskip} primitive@> +primitive("thickmuskip", assign_mu_glue, glue_base+thick_mu_skip_code);@/ +@!@:thick\_mu\_skip\_}{\.{\\thickmuskip} primitive@> + +@ @<Cases of |print_cmd_chr| for symbolic printing of primitives@>= +case assign_glue: case assign_mu_glue: if (chr_code < skip_base) + print_skip_param(chr_code-glue_base); + else if (chr_code < mu_skip_base) + {@+print_esc("skip");print_int(chr_code-skip_base); + } + else{@+print_esc("muskip");print_int(chr_code-mu_skip_base); + } @+break; + +@ All glue parameters and registers are initially `\.{0pt plus0pt minus0pt}'. + +@<Initialize table entries...@>= +equiv(glue_base)=zero_glue;eq_level(glue_base)=level_one; +eq_type(glue_base)=glue_ref; +for (k=glue_base+1; k<=local_base-1; k++) eqtb[k]=eqtb[glue_base]; +glue_ref_count(zero_glue)=glue_ref_count(zero_glue)+local_base-glue_base; + +@ @<Show equivalent |n|, in region 3@>= +if (n < skip_base) + {@+print_skip_param(n-glue_base);print_char('='); + if (n < glue_base+thin_mu_skip_code) print_spec(equiv(n),"pt"); + else print_spec(equiv(n),"mu"); + } +else if (n < mu_skip_base) + {@+print_esc("skip");print_int(n-skip_base);print_char('='); + print_spec(equiv(n),"pt"); + } +else{@+print_esc("muskip");print_int(n-mu_skip_base);print_char('='); + print_spec(equiv(n),"mu"); + } + +@ Region 4 of |eqtb| contains the local quantities defined here. The +bulk of this region is taken up by five tables that are indexed by eight-bit +characters; these tables are important to both the syntactic and semantic +portions of \TeX. There are also a bunch of special things like font and +token parameters, as well as the tables of \.{\\toks} and \.{\\box} +registers. + +@d par_shape_loc local_base /*specifies paragraph shape*/ +@d output_routine_loc (local_base+1) /*points to token list for \.{\\output}*/ +@d every_par_loc (local_base+2) /*points to token list for \.{\\everypar}*/ +@d every_math_loc (local_base+3) /*points to token list for \.{\\everymath}*/ +@d every_display_loc (local_base+4) /*points to token list for \.{\\everydisplay}*/ +@d every_hbox_loc (local_base+5) /*points to token list for \.{\\everyhbox}*/ +@d every_vbox_loc (local_base+6) /*points to token list for \.{\\everyvbox}*/ +@d every_job_loc (local_base+7) /*points to token list for \.{\\everyjob}*/ +@d every_cr_loc (local_base+8) /*points to token list for \.{\\everycr}*/ +@d err_help_loc (local_base+9) /*points to token list for \.{\\errhelp}*/ +@d tex_toks (local_base+10) /*end of \TeX's token list parameters*/ +@# +@d etex_toks_base pdf_toks /*base for \eTeX's token list parameters*/ +@d every_eof_loc etex_toks_base /*points to token list for \.{\\everyeof}*/ +@d etex_toks (etex_toks_base+1) /*end of \eTeX's token list parameters*/ +@# +@d toks_base etex_toks /*table of 256 token list registers*/ +@# +@d etex_pen_base (toks_base+256) /*start of table of \eTeX's penalties*/ +@d inter_line_penalties_loc etex_pen_base /*additional penalties between lines*/ +@d club_penalties_loc (etex_pen_base+1) /*penalties for creating club lines*/ +@d widow_penalties_loc (etex_pen_base+2) /*penalties for creating widow lines*/ +@d display_widow_penalties_loc (etex_pen_base+3) /*ditto, just before a display*/ +@d etex_pens (etex_pen_base+4) /*end of table of \eTeX's penalties*/ +@# +@d box_base etex_pens /*table of 256 box registers*/ +@d cur_font_loc (box_base+256) /*internal font number outside math mode*/ +@d math_font_base (cur_font_loc+1) /*table of 48 math font numbers*/ +@d cat_code_base (math_font_base+48) + /*table of 256 command codes (the ``catcodes'')*/ +@d lc_code_base (cat_code_base+256) /*table of 256 lowercase mappings*/ +@d uc_code_base (lc_code_base+256) /*table of 256 uppercase mappings*/ +@d sf_code_base (uc_code_base+256) /*table of 256 spacefactor mappings*/ +@d math_code_base (sf_code_base+256) /*table of 256 math mode mappings*/ +@d int_base (math_code_base+256) /*beginning of region 5*/ +@# +@d par_shape_ptr equiv(par_shape_loc) +@d output_routine equiv(output_routine_loc) +@d every_par equiv(every_par_loc) +@d every_math equiv(every_math_loc) +@d every_display equiv(every_display_loc) +@d every_hbox equiv(every_hbox_loc) +@d every_vbox equiv(every_vbox_loc) +@d every_job equiv(every_job_loc) +@d every_cr equiv(every_cr_loc) +@d err_help equiv(err_help_loc) +@d toks(X) equiv(toks_base+X) +@d box(A) equiv(box_base+A) +@d cur_font equiv(cur_font_loc) +@d fam_fnt(A) equiv(math_font_base+A) +@d cat_code(A) equiv(cat_code_base+A) +@d lc_code(A) equiv(lc_code_base+A) +@d uc_code(A) equiv(uc_code_base+A) +@d sf_code(A) equiv(sf_code_base+A) +@d math_code(A) equiv(math_code_base+A) + /*Note: |math_code(c)| is the true math code plus |min_halfword|*/ + +@<Put each...@>= +primitive("output", assign_toks, output_routine_loc); +@!@:output\_}{\.{\\output} primitive@> +primitive("everypar", assign_toks, every_par_loc); +@!@:every\_par\_}{\.{\\everypar} primitive@> +primitive("everymath", assign_toks, every_math_loc); +@!@:every\_math\_}{\.{\\everymath} primitive@> +primitive("everydisplay", assign_toks, every_display_loc); +@!@:every\_display\_}{\.{\\everydisplay} primitive@> +primitive("everyhbox", assign_toks, every_hbox_loc); +@!@:every\_hbox\_}{\.{\\everyhbox} primitive@> +primitive("everyvbox", assign_toks, every_vbox_loc); +@!@:every\_vbox\_}{\.{\\everyvbox} primitive@> +primitive("everyjob", assign_toks, every_job_loc); +@!@:every\_job\_}{\.{\\everyjob} primitive@> +primitive("everycr", assign_toks, every_cr_loc); +@!@:every\_cr\_}{\.{\\everycr} primitive@> +primitive("errhelp", assign_toks, err_help_loc); +@!@:err\_help\_}{\.{\\errhelp} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case assign_toks: if (chr_code >= toks_base) + {@+print_esc("toks");print_int(chr_code-toks_base); + } +else switch (chr_code) { + case output_routine_loc: print_esc("output");@+break; + case every_par_loc: print_esc("everypar");@+break; + case every_math_loc: print_esc("everymath");@+break; + case every_display_loc: print_esc("everydisplay");@+break; + case every_hbox_loc: print_esc("everyhbox");@+break; + case every_vbox_loc: print_esc("everyvbox");@+break; + case every_job_loc: print_esc("everyjob");@+break; + case every_cr_loc: print_esc("everycr");@+break; + @/@<Cases of |assign_toks| for |print_cmd_chr|@>@/ + default:print_esc("errhelp"); + } @+break; + +@ We initialize most things to null or undefined values. An undefined font +is represented by the internal code |font_base|. + +However, the character code tables are given initial values based on the +conventional interpretation of ASCII code. These initial values should +not be changed when \TeX\ is adapted for use with non-English languages; +all changes to the initialization conventions should be made in format +packages, not in \TeX\ itself, so that global interchange of formats is +possible. + +@d null_font font_base +@d var_code 070000 /*math code meaning ``use the current family''*/ + +@<Initialize table entries...@>= +par_shape_ptr=null;eq_type(par_shape_loc)=shape_ref; +eq_level(par_shape_loc)=level_one;@/ +for (k=etex_pen_base; k<=etex_pens-1; k++) + eqtb[k]=eqtb[par_shape_loc]; +for (k=output_routine_loc; k<=toks_base+255; k++) + eqtb[k]=eqtb[undefined_control_sequence]; +box(0)=null;eq_type(box_base)=box_ref;eq_level(box_base)=level_one; +for (k=box_base+1; k<=box_base+255; k++) eqtb[k]=eqtb[box_base]; +cur_font=null_font;eq_type(cur_font_loc)=data; +eq_level(cur_font_loc)=level_one;@/ +for (k=math_font_base; k<=math_font_base+47; k++) eqtb[k]=eqtb[cur_font_loc]; +equiv(cat_code_base)=0;eq_type(cat_code_base)=data; +eq_level(cat_code_base)=level_one;@/ +for (k=cat_code_base+1; k<=int_base-1; k++) eqtb[k]=eqtb[cat_code_base]; +for (k=0; k<=255; k++) + {@+cat_code(k)=other_char;math_code(k)=hi(k);sf_code(k)=1000; + } +cat_code(carriage_return)=car_ret;cat_code(' ')=spacer; +cat_code('\\')=escape;cat_code('%')=comment; +cat_code(invalid_code)=invalid_char;cat_code(null_code)=ignore; +for (k='0'; k<='9'; k++) math_code(k)=hi(k+var_code); +for (k='A'; k<='Z'; k++) + {@+cat_code(k)=letter;cat_code(k+'a'-'A')=letter;@/ + math_code(k)=hi(k+var_code+0x100); + math_code(k+'a'-'A')=hi(k+'a'-'A'+var_code+0x100);@/ + lc_code(k)=k+'a'-'A';lc_code(k+'a'-'A')=k+'a'-'A';@/ + uc_code(k)=k;uc_code(k+'a'-'A')=k;@/ + sf_code(k)=999; + } + +@ @<Show equivalent |n|, in region 4@>= +if ((n==par_shape_loc)||((n >= etex_pen_base)&&(n < etex_pens))) + {@+print_cmd_chr(set_shape, n);print_char('='); + if (equiv(n)==null) print_char('0'); + else if (n > par_shape_loc) + {@+print_int(penalty(equiv(n)));print_char(' '); + print_int(penalty(equiv(n)+1)); + if (penalty(equiv(n)) > 1) print_esc("ETC."); + } + else print_int(info(par_shape_ptr)); + } +else if (n < toks_base) + {@+print_cmd_chr(assign_toks, n);print_char('='); + if (equiv(n)!=null) show_token_list(link(equiv(n)), null, 32); + } +else if (n < box_base) + {@+print_esc("toks");print_int(n-toks_base);print_char('='); + if (equiv(n)!=null) show_token_list(link(equiv(n)), null, 32); + } +else if (n < cur_font_loc) + {@+print_esc("box");print_int(n-box_base);print_char('='); + if (equiv(n)==null) print("void"); + else{@+depth_threshold=0;breadth_max=1;show_node_list(equiv(n)); + } + } +else if (n < cat_code_base) @<Show the font identifier in |eqtb[n]|@>@; +else@<Show the halfword code in |eqtb[n]|@>@; + +@ @<Show the font identifier in |eqtb[n]|@>= +{@+if (n==cur_font_loc) print("current font"); +else if (n < math_font_base+16) + {@+print_esc("textfont");print_int(n-math_font_base); + } +else if (n < math_font_base+32) + {@+print_esc("scriptfont");print_int(n-math_font_base-16); + } +else{@+print_esc("scriptscriptfont");print_int(n-math_font_base-32); + } +print_char('=');@/ +printn_esc(hash[font_id_base+equiv(n)].rh); + /*that's |font_id_text(equiv(n))|*/ +} + +@ @<Show the halfword code in |eqtb[n]|@>= +if (n < math_code_base) + {@+if (n < lc_code_base) + {@+print_esc("catcode");print_int(n-cat_code_base); + } + else if (n < uc_code_base) + {@+print_esc("lccode");print_int(n-lc_code_base); + } + else if (n < sf_code_base) + {@+print_esc("uccode");print_int(n-uc_code_base); + } + else{@+print_esc("sfcode");print_int(n-sf_code_base); + } + print_char('=');print_int(equiv(n)); + } +else{@+print_esc("mathcode");print_int(n-math_code_base); + print_char('=');print_int(ho(equiv(n))); + } + +@ Region 5 of |eqtb| contains the integer parameters and registers defined +here, as well as the |del_code| table. The latter table differs from the +|cat_code dotdot math_code| tables that precede it, since delimiter codes are +fullword integers while the other kinds of codes occupy at most a +halfword. This is what makes region~5 different from region~4. We will +store the |eq_level| information in an auxiliary array of quarterwords +that will be defined later. + +@d pretolerance_code 0 /*badness tolerance before hyphenation*/ +@d tolerance_code 1 /*badness tolerance after hyphenation*/ +@d line_penalty_code 2 /*added to the badness of every line*/ +@d hyphen_penalty_code 3 /*penalty for break after discretionary hyphen*/ +@d ex_hyphen_penalty_code 4 /*penalty for break after explicit hyphen*/ +@d club_penalty_code 5 /*penalty for creating a club line*/ +@d widow_penalty_code 6 /*penalty for creating a widow line*/ +@d display_widow_penalty_code 7 /*ditto, just before a display*/ +@d broken_penalty_code 8 /*penalty for breaking a page at a broken line*/ +@d bin_op_penalty_code 9 /*penalty for breaking after a binary operation*/ +@d rel_penalty_code 10 /*penalty for breaking after a relation*/ +@d pre_display_penalty_code 11 + /*penalty for breaking just before a displayed formula*/ +@d post_display_penalty_code 12 + /*penalty for breaking just after a displayed formula*/ +@d inter_line_penalty_code 13 /*additional penalty between lines*/ +@d double_hyphen_demerits_code 14 /*demerits for double hyphen break*/ +@d final_hyphen_demerits_code 15 /*demerits for final hyphen break*/ +@d adj_demerits_code 16 /*demerits for adjacent incompatible lines*/ +@d mag_code 17 /*magnification ratio*/ +@d delimiter_factor_code 18 /*ratio for variable-size delimiters*/ +@d looseness_code 19 /*change in number of lines for a paragraph*/ +@d time_code 20 /*current time of day*/ +@d day_code 21 /*current day of the month*/ +@d month_code 22 /*current month of the year*/ +@d year_code 23 /*current year of our Lord*/ +@d show_box_breadth_code 24 /*nodes per level in |show_box|*/ +@d show_box_depth_code 25 /*maximum level in |show_box|*/ +@d hbadness_code 26 /*hboxes exceeding this badness will be shown by |hpack|*/ +@d vbadness_code 27 /*vboxes exceeding this badness will be shown by |vpack|*/ +@d pausing_code 28 /*pause after each line is read from a file*/ +@d tracing_online_code 29 /*show diagnostic output on terminal*/ +@d tracing_macros_code 30 /*show macros as they are being expanded*/ +@d tracing_stats_code 31 /*show memory usage if \TeX\ knows it*/ +@d tracing_paragraphs_code 32 /*show line-break calculations*/ +@d tracing_pages_code 33 /*show page-break calculations*/ +@d tracing_output_code 34 /*show boxes when they are shipped out*/ +@d tracing_lost_chars_code 35 /*show characters that aren't in the font*/ +@d tracing_commands_code 36 /*show command codes at |big_switch|*/ +@d tracing_restores_code 37 /*show equivalents when they are restored*/ +@d uc_hyph_code 38 /*hyphenate words beginning with a capital letter*/ +@d output_penalty_code 39 /*penalty found at current page break*/ +@d max_dead_cycles_code 40 /*bound on consecutive dead cycles of output*/ +@d hang_after_code 41 /*hanging indentation changes after this many lines*/ +@d floating_penalty_code 42 /*penalty for insertions held over after a split*/ +@d global_defs_code 43 /*override \.{\\global} specifications*/ +@d cur_fam_code 44 /*current family*/ +@d escape_char_code 45 /*escape character for token output*/ +@d default_hyphen_char_code 46 /*value of \.{\\hyphenchar} when a font is loaded*/ +@d default_skew_char_code 47 /*value of \.{\\skewchar} when a font is loaded*/ +@d end_line_char_code 48 /*character placed at the right end of the buffer*/ +@d new_line_char_code 49 /*character that prints as |print_ln|*/ +@d language_code 50 /*current hyphenation table*/ +@d left_hyphen_min_code 51 /*minimum left hyphenation fragment size*/ +@d right_hyphen_min_code 52 /*minimum right hyphenation fragment size*/ +@d holding_inserts_code 53 /*do not remove insertion nodes from \.{\\box255}*/ +@d error_context_lines_code 54 /*maximum intermediate line pairs shown*/ +@d tex_int_pars 55 /*total number of \TeX's integer parameters*/ +@# +@d etex_int_base pdf_int_pars /*base for \eTeX's integer parameters*/ +@d tracing_assigns_code etex_int_base /*show assignments*/ +@d tracing_groups_code (etex_int_base+1) /*show save/restore groups*/ +@d tracing_ifs_code (etex_int_base+2) /*show conditionals*/ +@d tracing_scan_tokens_code (etex_int_base+3) /*show pseudo file open and close*/ +@d tracing_nesting_code (etex_int_base+4) /*show incomplete groups and ifs within files*/ +@d saving_vdiscards_code (etex_int_base+5) /*save items discarded from vlists*/ +@d saving_hyph_codes_code (etex_int_base+6) /*save hyphenation codes for languages*/ +@d expand_depth_code (etex_int_base+7) /*maximum depth for expansion---\eTeX*/ +@d eTeX_state_code (etex_int_base+8) /*\eTeX\ state variables*/ +@d etex_int_pars (eTeX_state_code+eTeX_states) /*total number of \eTeX's integer parameters*/ +@# +@d int_pars etex_int_pars /*total number of integer parameters*/ +@d count_base (int_base+int_pars) /*256 user \.{\\count} registers*/ +@d del_code_base (count_base+256) /*256 delimiter code mappings*/ +@d dimen_base (del_code_base+256) /*beginning of region 6*/ +@# +@d del_code(A) eqtb[del_code_base+A].i +@d count(A) eqtb[count_base+A].i +@d int_par(A) eqtb[int_base+A].i /*an integer parameter*/ +@d pretolerance int_par(pretolerance_code) +@d tolerance int_par(tolerance_code) +@d line_penalty int_par(line_penalty_code) +@d hyphen_penalty int_par(hyphen_penalty_code) +@d ex_hyphen_penalty int_par(ex_hyphen_penalty_code) +@d club_penalty int_par(club_penalty_code) +@d widow_penalty int_par(widow_penalty_code) +@d display_widow_penalty int_par(display_widow_penalty_code) +@d broken_penalty int_par(broken_penalty_code) +@d bin_op_penalty int_par(bin_op_penalty_code) +@d rel_penalty int_par(rel_penalty_code) +@d pre_display_penalty int_par(pre_display_penalty_code) +@d post_display_penalty int_par(post_display_penalty_code) +@d inter_line_penalty int_par(inter_line_penalty_code) +@d double_hyphen_demerits int_par(double_hyphen_demerits_code) +@d final_hyphen_demerits int_par(final_hyphen_demerits_code) +@d adj_demerits int_par(adj_demerits_code) +@d mag int_par(mag_code) +@d delimiter_factor int_par(delimiter_factor_code) +@d looseness int_par(looseness_code) +@d time int_par(time_code) +@d day int_par(day_code) +@d month int_par(month_code) +@d year int_par(year_code) +@d show_box_breadth int_par(show_box_breadth_code) +@d show_box_depth int_par(show_box_depth_code) +@d hbadness int_par(hbadness_code) +@d vbadness int_par(vbadness_code) +@d pausing int_par(pausing_code) +@d tracing_online int_par(tracing_online_code) +@d tracing_macros int_par(tracing_macros_code) +@d tracing_stats int_par(tracing_stats_code) +@d tracing_paragraphs int_par(tracing_paragraphs_code) +@d tracing_pages int_par(tracing_pages_code) +@d tracing_output int_par(tracing_output_code) +@d tracing_lost_chars int_par(tracing_lost_chars_code) +@d tracing_commands int_par(tracing_commands_code) +@d tracing_restores int_par(tracing_restores_code) +@d uc_hyph int_par(uc_hyph_code) +@d output_penalty int_par(output_penalty_code) +@d max_dead_cycles int_par(max_dead_cycles_code) +@d hang_after int_par(hang_after_code) +@d floating_penalty int_par(floating_penalty_code) +@d global_defs int_par(global_defs_code) +@d cur_fam int_par(cur_fam_code) +@d escape_char int_par(escape_char_code) +@d default_hyphen_char int_par(default_hyphen_char_code) +@d default_skew_char int_par(default_skew_char_code) +@d end_line_char int_par(end_line_char_code) +@d new_line_char int_par(new_line_char_code) +@d language int_par(language_code) +@d left_hyphen_min int_par(left_hyphen_min_code) +@d right_hyphen_min int_par(right_hyphen_min_code) +@d holding_inserts int_par(holding_inserts_code) +@d error_context_lines int_par(error_context_lines_code) +@# +@d tracing_assigns int_par(tracing_assigns_code) +@d tracing_groups int_par(tracing_groups_code) +@d tracing_ifs int_par(tracing_ifs_code) +@d tracing_scan_tokens int_par(tracing_scan_tokens_code) +@d tracing_nesting int_par(tracing_nesting_code) +@d saving_vdiscards int_par(saving_vdiscards_code) +@d saving_hyph_codes int_par(saving_hyph_codes_code) +@d expand_depth int_par(expand_depth_code) + +@<Assign the values |depth_threshold:=show_box_depth|...@>= +depth_threshold=show_box_depth; +breadth_max=show_box_breadth + +@ We can print the symbolic name of an integer parameter as follows. + +@p static void print_param(int @!n) +{@+switch (n) { +case pretolerance_code: print_esc("pretolerance");@+break; +case tolerance_code: print_esc("tolerance");@+break; +case line_penalty_code: print_esc("linepenalty");@+break; +case hyphen_penalty_code: print_esc("hyphenpenalty");@+break; +case ex_hyphen_penalty_code: print_esc("exhyphenpenalty");@+break; +case club_penalty_code: print_esc("clubpenalty");@+break; +case widow_penalty_code: print_esc("widowpenalty");@+break; +case display_widow_penalty_code: print_esc("displaywidowpenalty");@+break; +case broken_penalty_code: print_esc("brokenpenalty");@+break; +case bin_op_penalty_code: print_esc("binoppenalty");@+break; +case rel_penalty_code: print_esc("relpenalty");@+break; +case pre_display_penalty_code: print_esc("predisplaypenalty");@+break; +case post_display_penalty_code: print_esc("postdisplaypenalty");@+break; +case inter_line_penalty_code: print_esc("interlinepenalty");@+break; +case double_hyphen_demerits_code: print_esc("doublehyphendemerits");@+break; +case final_hyphen_demerits_code: print_esc("finalhyphendemerits");@+break; +case adj_demerits_code: print_esc("adjdemerits");@+break; +case mag_code: print_esc("mag");@+break; +case delimiter_factor_code: print_esc("delimiterfactor");@+break; +case looseness_code: print_esc("looseness");@+break; +case time_code: print_esc("time");@+break; +case day_code: print_esc("day");@+break; +case month_code: print_esc("month");@+break; +case year_code: print_esc("year");@+break; +case show_box_breadth_code: print_esc("showboxbreadth");@+break; +case show_box_depth_code: print_esc("showboxdepth");@+break; +case hbadness_code: print_esc("hbadness");@+break; +case vbadness_code: print_esc("vbadness");@+break; +case pausing_code: print_esc("pausing");@+break; +case tracing_online_code: print_esc("tracingonline");@+break; +case tracing_macros_code: print_esc("tracingmacros");@+break; +case tracing_stats_code: print_esc("tracingstats");@+break; +case tracing_paragraphs_code: print_esc("tracingparagraphs");@+break; +case tracing_pages_code: print_esc("tracingpages");@+break; +case tracing_output_code: print_esc("tracingoutput");@+break; +case tracing_lost_chars_code: print_esc("tracinglostchars");@+break; +case tracing_commands_code: print_esc("tracingcommands");@+break; +case tracing_restores_code: print_esc("tracingrestores");@+break; +case uc_hyph_code: print_esc("uchyph");@+break; +case output_penalty_code: print_esc("outputpenalty");@+break; +case max_dead_cycles_code: print_esc("maxdeadcycles");@+break; +case hang_after_code: print_esc("hangafter");@+break; +case floating_penalty_code: print_esc("floatingpenalty");@+break; +case global_defs_code: print_esc("globaldefs");@+break; +case cur_fam_code: print_esc("fam");@+break; +case escape_char_code: print_esc("escapechar");@+break; +case default_hyphen_char_code: print_esc("defaulthyphenchar");@+break; +case default_skew_char_code: print_esc("defaultskewchar");@+break; +case end_line_char_code: print_esc("endlinechar");@+break; +case new_line_char_code: print_esc("newlinechar");@+break; +case language_code: print_esc("language");@+break; +case left_hyphen_min_code: print_esc("lefthyphenmin");@+break; +case right_hyphen_min_code: print_esc("righthyphenmin");@+break; +case holding_inserts_code: print_esc("holdinginserts");@+break; +case error_context_lines_code: print_esc("errorcontextlines");@+break; +@/@<Cases for |print_param|@>@/ +default:print("[unknown integer parameter!]"); +} +} + +@ The integer parameter names must be entered into the hash table. + +@<Put each...@>= +primitive("pretolerance", assign_int, int_base+pretolerance_code);@/ +@!@:pretolerance\_}{\.{\\pretolerance} primitive@> +primitive("tolerance", assign_int, int_base+tolerance_code);@/ +@!@:tolerance\_}{\.{\\tolerance} primitive@> +primitive("linepenalty", assign_int, int_base+line_penalty_code);@/ +@!@:line\_penalty\_}{\.{\\linepenalty} primitive@> +primitive("hyphenpenalty", assign_int, int_base+hyphen_penalty_code);@/ +@!@:hyphen\_penalty\_}{\.{\\hyphenpenalty} primitive@> +primitive("exhyphenpenalty", assign_int, int_base+ex_hyphen_penalty_code);@/ +@!@:ex\_hyphen\_penalty\_}{\.{\\exhyphenpenalty} primitive@> +primitive("clubpenalty", assign_int, int_base+club_penalty_code);@/ +@!@:club\_penalty\_}{\.{\\clubpenalty} primitive@> +primitive("widowpenalty", assign_int, int_base+widow_penalty_code);@/ +@!@:widow\_penalty\_}{\.{\\widowpenalty} primitive@> +primitive("displaywidowpenalty", + assign_int, int_base+display_widow_penalty_code);@/ +@!@:display\_widow\_penalty\_}{\.{\\displaywidowpenalty} primitive@> +primitive("brokenpenalty", assign_int, int_base+broken_penalty_code);@/ +@!@:broken\_penalty\_}{\.{\\brokenpenalty} primitive@> +primitive("binoppenalty", assign_int, int_base+bin_op_penalty_code);@/ +@!@:bin\_op\_penalty\_}{\.{\\binoppenalty} primitive@> +primitive("relpenalty", assign_int, int_base+rel_penalty_code);@/ +@!@:rel\_penalty\_}{\.{\\relpenalty} primitive@> +primitive("predisplaypenalty", assign_int, int_base+pre_display_penalty_code);@/ +@!@:pre\_display\_penalty\_}{\.{\\predisplaypenalty} primitive@> +primitive("postdisplaypenalty", assign_int, int_base+post_display_penalty_code);@/ +@!@:post\_display\_penalty\_}{\.{\\postdisplaypenalty} primitive@> +primitive("interlinepenalty", assign_int, int_base+inter_line_penalty_code);@/ +@!@:inter\_line\_penalty\_}{\.{\\interlinepenalty} primitive@> +primitive("doublehyphendemerits", + assign_int, int_base+double_hyphen_demerits_code);@/ +@!@:double\_hyphen\_demerits\_}{\.{\\doublehyphendemerits} primitive@> +primitive("finalhyphendemerits", + assign_int, int_base+final_hyphen_demerits_code);@/ +@!@:final\_hyphen\_demerits\_}{\.{\\finalhyphendemerits} primitive@> +primitive("adjdemerits", assign_int, int_base+adj_demerits_code);@/ +@!@:adj\_demerits\_}{\.{\\adjdemerits} primitive@> +primitive("mag", assign_int, int_base+mag_code);@/ +@!@:mag\_}{\.{\\mag} primitive@> +primitive("delimiterfactor", assign_int, int_base+delimiter_factor_code);@/ +@!@:delimiter\_factor\_}{\.{\\delimiterfactor} primitive@> +primitive("looseness", assign_int, int_base+looseness_code);@/ +@!@:looseness\_}{\.{\\looseness} primitive@> +primitive("time", assign_int, int_base+time_code);@/ +@!@:time\_}{\.{\\time} primitive@> +primitive("day", assign_int, int_base+day_code);@/ +@!@:day\_}{\.{\\day} primitive@> +primitive("month", assign_int, int_base+month_code);@/ +@!@:month\_}{\.{\\month} primitive@> +primitive("year", assign_int, int_base+year_code);@/ +@!@:year\_}{\.{\\year} primitive@> +primitive("showboxbreadth", assign_int, int_base+show_box_breadth_code);@/ +@!@:show\_box\_breadth\_}{\.{\\showboxbreadth} primitive@> +primitive("showboxdepth", assign_int, int_base+show_box_depth_code);@/ +@!@:show\_box\_depth\_}{\.{\\showboxdepth} primitive@> +primitive("hbadness", assign_int, int_base+hbadness_code);@/ +@!@:hbadness\_}{\.{\\hbadness} primitive@> +primitive("vbadness", assign_int, int_base+vbadness_code);@/ +@!@:vbadness\_}{\.{\\vbadness} primitive@> +primitive("pausing", assign_int, int_base+pausing_code);@/ +@!@:pausing\_}{\.{\\pausing} primitive@> +primitive("tracingonline", assign_int, int_base+tracing_online_code);@/ +@!@:tracing\_online\_}{\.{\\tracingonline} primitive@> +primitive("tracingmacros", assign_int, int_base+tracing_macros_code);@/ +@!@:tracing\_macros\_}{\.{\\tracingmacros} primitive@> +primitive("tracingstats", assign_int, int_base+tracing_stats_code);@/ +@!@:tracing\_stats\_}{\.{\\tracingstats} primitive@> +primitive("tracingparagraphs", assign_int, int_base+tracing_paragraphs_code);@/ +@!@:tracing\_paragraphs\_}{\.{\\tracingparagraphs} primitive@> +primitive("tracingpages", assign_int, int_base+tracing_pages_code);@/ +@!@:tracing\_pages\_}{\.{\\tracingpages} primitive@> +primitive("tracingoutput", assign_int, int_base+tracing_output_code);@/ +@!@:tracing\_output\_}{\.{\\tracingoutput} primitive@> +primitive("tracinglostchars", assign_int, int_base+tracing_lost_chars_code);@/ +@!@:tracing\_lost\_chars\_}{\.{\\tracinglostchars} primitive@> +primitive("tracingcommands", assign_int, int_base+tracing_commands_code);@/ +@!@:tracing\_commands\_}{\.{\\tracingcommands} primitive@> +primitive("tracingrestores", assign_int, int_base+tracing_restores_code);@/ +@!@:tracing\_restores\_}{\.{\\tracingrestores} primitive@> +primitive("uchyph", assign_int, int_base+uc_hyph_code);@/ +@!@:uc\_hyph\_}{\.{\\uchyph} primitive@> +primitive("outputpenalty", assign_int, int_base+output_penalty_code);@/ +@!@:output\_penalty\_}{\.{\\outputpenalty} primitive@> +primitive("maxdeadcycles", assign_int, int_base+max_dead_cycles_code);@/ +@!@:max\_dead\_cycles\_}{\.{\\maxdeadcycles} primitive@> +primitive("hangafter", assign_int, int_base+hang_after_code);@/ +@!@:hang\_after\_}{\.{\\hangafter} primitive@> +primitive("floatingpenalty", assign_int, int_base+floating_penalty_code);@/ +@!@:floating\_penalty\_}{\.{\\floatingpenalty} primitive@> +primitive("globaldefs", assign_int, int_base+global_defs_code);@/ +@!@:global\_defs\_}{\.{\\globaldefs} primitive@> +primitive("fam", assign_int, int_base+cur_fam_code);@/ +@!@:fam\_}{\.{\\fam} primitive@> +primitive("escapechar", assign_int, int_base+escape_char_code);@/ +@!@:escape\_char\_}{\.{\\escapechar} primitive@> +primitive("defaulthyphenchar", assign_int, int_base+default_hyphen_char_code);@/ +@!@:default\_hyphen\_char\_}{\.{\\defaulthyphenchar} primitive@> +primitive("defaultskewchar", assign_int, int_base+default_skew_char_code);@/ +@!@:default\_skew\_char\_}{\.{\\defaultskewchar} primitive@> +primitive("endlinechar", assign_int, int_base+end_line_char_code);@/ +@!@:end\_line\_char\_}{\.{\\endlinechar} primitive@> +primitive("newlinechar", assign_int, int_base+new_line_char_code);@/ +@!@:new\_line\_char\_}{\.{\\newlinechar} primitive@> +primitive("language", assign_int, int_base+language_code);@/ +@!@:language\_}{\.{\\language} primitive@> +primitive("lefthyphenmin", assign_int, int_base+left_hyphen_min_code);@/ +@!@:left\_hyphen\_min\_}{\.{\\lefthyphenmin} primitive@> +primitive("righthyphenmin", assign_int, int_base+right_hyphen_min_code);@/ +@!@:right\_hyphen\_min\_}{\.{\\righthyphenmin} primitive@> +primitive("holdinginserts", assign_int, int_base+holding_inserts_code);@/ +@!@:holding\_inserts\_}{\.{\\holdinginserts} primitive@> +primitive("errorcontextlines", assign_int, int_base+error_context_lines_code);@/ +@!@:error\_context\_lines\_}{\.{\\errorcontextlines} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case assign_int: if (chr_code < count_base) print_param(chr_code-int_base); + else{@+print_esc("count");print_int(chr_code-count_base); + } @+break; + +@ The integer parameters should really be initialized by a macro package; +the following initialization does the minimum to keep \TeX\ from +complete failure. +@^null delimiter@> + +@<Initialize table entries...@>= +for (k=int_base; k<=del_code_base-1; k++) eqtb[k].i=0; +mag=1000;tolerance=10000;hang_after=1;max_dead_cycles=25; +escape_char='\\';end_line_char=carriage_return; +for (k=0; k<=255; k++) del_code(k)=-1; +del_code('.')=0; /*this null delimiter is used in error recovery*/ + +@ The following procedure, which is called just before \TeX\ initializes its +input and output, establishes the initial values of the date and time. +This does include too, for system integrators, the creation date and +the reference moment for the timer---\Prote\ extensions. If the system +supports environment variables, if |FORCE_SOURCE_DATE| is set to $1$ and +|SOURCE_DATE_EPOCH| is set, the date related values: year, month, day +and time, including creation date, will be taken relative from the value +defined by |SOURCE_DATE_EPOCH|. +@^creation date@> +@^reference time@> +@^system dependencies@> +\TeX\ Live calls |tl_now| to obtain the current time as a |tm| structure. +@p static void fix_date_and_time(void) +{@+ struct tm *t=tl_now(); + time=sys_time= t->tm_hour*60+t->tm_min;/*minutes since midnight*/ + day=sys_day= t->tm_mday;/*day of the month*/ + month=sys_month=t->tm_mon+1;/*month of the year*/ + year=sys_year=t->tm_year+1900;/*Anno Domini*/ +} + +@ @<Show equivalent |n|, in region 5@>= +{@+if (n < count_base) print_param(n-int_base); +else if (n < del_code_base) + {@+print_esc("count");print_int(n-count_base); + } +else{@+print_esc("delcode");print_int(n-del_code_base); + } +print_char('=');print_int(eqtb[n].i); +} + +@ @<Set variable |c| to the current escape character@>=c=escape_char + +@ @<Character |s| is the current new-line character@>=s==new_line_char + +@ \TeX\ is occasionally supposed to print diagnostic information that +goes only into the transcript file, unless |tracing_online| is positive. +Here are two routines that adjust the destination of print commands: + +@p static void begin_diagnostic(void) /*prepare to do some tracing*/ +{@+old_setting=selector; +if ((tracing_online <= 0)&&(selector==term_and_log)) + {@+decr(selector); + if (history==spotless) history=warning_issued; + } +} +@# +static void end_diagnostic(bool @!blank_line) + /*restore proper conditions after tracing*/ +{@+print_nl(""); +if (blank_line) print_ln(); +selector=old_setting; +} + +@ Of course we had better declare a few more global variables, if the previous +routines are going to work. + +@<Glob...@>= +static int @!old_setting; +static int @!sys_time, @!sys_day, @!sys_month, @!sys_year; + /*date and time supplied by external system*/ + +@ The final region of |eqtb| contains the dimension parameters defined +here, and the 256 \.{\\dimen} registers. + +@d par_indent_code 0 /*indentation of paragraphs*/ +@d math_surround_code 1 /*space around math in text*/ +@d line_skip_limit_code 2 /*threshold for |line_skip| instead of |baseline_skip|*/ +@d hsize_code 3 /*line width in horizontal mode*/ +@d vsize_code 4 /*page height in vertical mode*/ +@d max_depth_code 5 /*maximum depth of boxes on main pages*/ +@d split_max_depth_code 6 /*maximum depth of boxes on split pages*/ +@d box_max_depth_code 7 /*maximum depth of explicit vboxes*/ +@d hfuzz_code 8 /*tolerance for overfull hbox messages*/ +@d vfuzz_code 9 /*tolerance for overfull vbox messages*/ +@d delimiter_shortfall_code 10 /*maximum amount uncovered by variable delimiters*/ +@d null_delimiter_space_code 11 /*blank space in null delimiters*/ +@d script_space_code 12 /*extra space after subscript or superscript*/ +@d pre_display_size_code 13 /*length of text preceding a display*/ +@d display_width_code 14 /*length of line for displayed equation*/ +@d display_indent_code 15 /*indentation of line for displayed equation*/ +@d overfull_rule_code 16 /*width of rule that identifies overfull hboxes*/ +@d hang_indent_code 17 /*amount of hanging indentation*/ +@d h_offset_code 18 /*amount of horizontal offset when shipping pages out*/ +@d v_offset_code 19 /*amount of vertical offset when shipping pages out*/ +@d emergency_stretch_code 20 /*reduces badnesses on final pass of line-breaking*/ +@d page_width_code 21 /*current paper page width*/ +@d page_height_code 22 /*current paper page height*/ +@d dimen_pars (pdftex_last_dimen_code+1) /*total number of dimension parameters*/ +@d scaled_base (dimen_base+dimen_pars) + /*table of 256 user-defined \.{\\dimen} registers*/ +@d eqtb_size (scaled_base+255) /*largest subscript of |eqtb|*/ +@# +@d dimen(A) eqtb[scaled_base+A].sc +@d dimen_par(A) eqtb[dimen_base+A].sc /*a scaled quantity*/ +@d par_indent dimen_par(par_indent_code) +@d math_surround dimen_par(math_surround_code) +@d line_skip_limit dimen_par(line_skip_limit_code) +@d hsize dimen_par(hsize_code) +@d vsize dimen_par(vsize_code) +@d max_depth dimen_par(max_depth_code) +@d split_max_depth dimen_par(split_max_depth_code) +@d box_max_depth dimen_par(box_max_depth_code) +@d hfuzz dimen_par(hfuzz_code) +@d vfuzz dimen_par(vfuzz_code) +@d delimiter_shortfall dimen_par(delimiter_shortfall_code) +@d null_delimiter_space dimen_par(null_delimiter_space_code) +@d script_space dimen_par(script_space_code) +@d pre_display_size dimen_par(pre_display_size_code) +@d display_width dimen_par(display_width_code) +@d display_indent dimen_par(display_indent_code) +@d overfull_rule dimen_par(overfull_rule_code) +@d hang_indent dimen_par(hang_indent_code) +@d h_offset dimen_par(h_offset_code) +@d v_offset dimen_par(v_offset_code) +@d emergency_stretch dimen_par(emergency_stretch_code) +@d page_height dimen_par(page_height_code) + +@p static void print_length_param(int @!n) +{@+switch (n) { +case par_indent_code: print_esc("parindent");@+break; +case math_surround_code: print_esc("mathsurround");@+break; +case line_skip_limit_code: print_esc("lineskiplimit");@+break; +case hsize_code: print_esc("hsize");@+break; +case vsize_code: print_esc("vsize");@+break; +case max_depth_code: print_esc("maxdepth");@+break; +case split_max_depth_code: print_esc("splitmaxdepth");@+break; +case box_max_depth_code: print_esc("boxmaxdepth");@+break; +case hfuzz_code: print_esc("hfuzz");@+break; +case vfuzz_code: print_esc("vfuzz");@+break; +case delimiter_shortfall_code: print_esc("delimitershortfall");@+break; +case null_delimiter_space_code: print_esc("nulldelimiterspace");@+break; +case script_space_code: print_esc("scriptspace");@+break; +case pre_display_size_code: print_esc("predisplaysize");@+break; +case display_width_code: print_esc("displaywidth");@+break; +case display_indent_code: print_esc("displayindent");@+break; +case overfull_rule_code: print_esc("overfullrule");@+break; +case hang_indent_code: print_esc("hangindent");@+break; +case h_offset_code: print_esc("hoffset");@+break; +case v_offset_code: print_esc("voffset");@+break; +case emergency_stretch_code: print_esc("emergencystretch");@+break; +case page_width_code: + if (pdf_on) print_esc("pdfpagewidth"); + else print_esc("pagewidth"); + @+break; +case page_height_code: + if (pdf_on) print_esc("pdfpageheight"); + else print_esc("pageheight"); + @+break; +@<Cases for |print_lenght_param|@>@; +default:print("[unknown dimen parameter!]"); +} +} + +@ @<Put each...@>= +primitive("parindent", assign_dimen, dimen_base+par_indent_code);@/ +@!@:par\_indent\_}{\.{\\parindent} primitive@> +primitive("mathsurround", assign_dimen, dimen_base+math_surround_code);@/ +@!@:math\_surround\_}{\.{\\mathsurround} primitive@> +primitive("lineskiplimit", assign_dimen, dimen_base+line_skip_limit_code);@/ +@!@:line\_skip\_limit\_}{\.{\\lineskiplimit} primitive@> +primitive("hsize", assign_dimen, dimen_base+hsize_code);@/ +@!@:hsize\_}{\.{\\hsize} primitive@> +primitive("vsize", assign_dimen, dimen_base+vsize_code);@/ +@!@:vsize\_}{\.{\\vsize} primitive@> +primitive("maxdepth", assign_dimen, dimen_base+max_depth_code);@/ +@!@:max\_depth\_}{\.{\\maxdepth} primitive@> +primitive("splitmaxdepth", assign_dimen, dimen_base+split_max_depth_code);@/ +@!@:split\_max\_depth\_}{\.{\\splitmaxdepth} primitive@> +primitive("boxmaxdepth", assign_dimen, dimen_base+box_max_depth_code);@/ +@!@:box\_max\_depth\_}{\.{\\boxmaxdepth} primitive@> +primitive("hfuzz", assign_dimen, dimen_base+hfuzz_code);@/ +@!@:hfuzz\_}{\.{\\hfuzz} primitive@> +primitive("vfuzz", assign_dimen, dimen_base+vfuzz_code);@/ +@!@:vfuzz\_}{\.{\\vfuzz} primitive@> +primitive("delimitershortfall", + assign_dimen, dimen_base+delimiter_shortfall_code);@/ +@!@:delimiter\_shortfall\_}{\.{\\delimitershortfall} primitive@> +primitive("nulldelimiterspace", + assign_dimen, dimen_base+null_delimiter_space_code);@/ +@!@:null\_delimiter\_space\_}{\.{\\nulldelimiterspace} primitive@> +primitive("scriptspace", assign_dimen, dimen_base+script_space_code);@/ +@!@:script\_space\_}{\.{\\scriptspace} primitive@> +primitive("predisplaysize", assign_dimen, dimen_base+pre_display_size_code);@/ +@!@:pre\_display\_size\_}{\.{\\predisplaysize} primitive@> +primitive("displaywidth", assign_dimen, dimen_base+display_width_code);@/ +@!@:display\_width\_}{\.{\\displaywidth} primitive@> +primitive("displayindent", assign_dimen, dimen_base+display_indent_code);@/ +@!@:display\_indent\_}{\.{\\displayindent} primitive@> +primitive("overfullrule", assign_dimen, dimen_base+overfull_rule_code);@/ +@!@:overfull\_rule\_}{\.{\\overfullrule} primitive@> +primitive("hangindent", assign_dimen, dimen_base+hang_indent_code);@/ +@!@:hang\_indent\_}{\.{\\hangindent} primitive@> +primitive("hoffset", assign_dimen, dimen_base+h_offset_code);@/ +@!@:h\_offset\_}{\.{\\hoffset} primitive@> +primitive("voffset", assign_dimen, dimen_base+v_offset_code);@/ +@!@:v\_offset\_}{\.{\\voffset} primitive@> +primitive("emergencystretch", assign_dimen, dimen_base+emergency_stretch_code);@/ +@!@:emergency\_stretch\_}{\.{\\emergencystretch} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case assign_dimen: if (chr_code < scaled_base) + print_length_param(chr_code-dimen_base); + else{@+print_esc("dimen");print_int(chr_code-scaled_base); + } @+break; + +@ @<Initialize table entries...@>= +for (k=dimen_base; k<=eqtb_size; k++) eqtb[k].sc=0; + +@ @<Show equivalent |n|, in region 6@>= +{@+if (n < scaled_base) print_length_param(n-dimen_base); +else{@+print_esc("dimen");print_int(n-scaled_base); + } +print_char('=');print_scaled(eqtb[n].sc);print("pt"); +} + +@ Here is a procedure that displays the contents of |eqtb[n]| +symbolically. + +@p @t\4@>@<Declare the procedure called |print_cmd_chr|@>@;@/ +#ifdef @!STAT +static void show_eqtb(pointer @!n) +{@+if (n < active_base) print_char('?'); /*this can't happen*/ +else if (n < glue_base) @<Show equivalent |n|, in region 1 or 2@>@; +else if (n < local_base) @<Show equivalent |n|, in region 3@>@; +else if (n < int_base) @<Show equivalent |n|, in region 4@>@; +else if (n < dimen_base) @<Show equivalent |n|, in region 5@>@; +else if (n <= eqtb_size) @<Show equivalent |n|, in region 6@>@; +else print_char('?'); /*this can't happen either*/ +} +#endif + +@ The last two regions of |eqtb| have fullword values instead of the +three fields |eq_level|, |eq_type|, and |equiv|. An |eq_type| is unnecessary, +but \TeX\ needs to store the |eq_level| information in another array +called |xeq_level|. + +@<Glob...@>= +static memory_word @!eqtb0[eqtb_size-active_base+1], + *const @!eqtb = @!eqtb0-active_base; +static quarterword @!xeq_level0[eqtb_size-int_base+1], + *const @!xeq_level = @!xeq_level0-int_base; + +@ @<Set init...@>= +for (k=int_base; k<=eqtb_size; k++) xeq_level[k]=level_one; + +@ When the debugging routine |search_mem| is looking for pointers having a +given value, it is interested only in regions 1 to~3 of~|eqtb|, and in the +first part of region~4. + +@<Search |eqtb| for equivalents equal to |p|@>= +for (q=active_base; q<=box_base+255; q++) + {@+if (equiv(q)==p) + {@+print_nl("EQUIV(");print_int(q);print_char(')'); + } + } + +@* The hash table. +Control sequences are stored and retrieved by means of a fairly standard hash +table algorithm called the method of ``coalescing lists'' (cf.\ Algorithm 6.4C +in {\sl The Art of Computer Programming\/}). Once a control sequence enters the +table, it is never removed, because there are complicated situations +involving \.{\\gdef} where the removal of a control sequence at the end of +a group would be a mistake preventable only by the introduction of a +complicated reference-count mechanism. + +The actual sequence of letters forming a control sequence identifier is +stored in the |str_pool| array together with all the other strings. An +auxiliary array |hash| consists of items with two halfword fields per +word. The first of these, called |next(p)|, points to the next identifier +belonging to the same coalesced list as the identifier corresponding to~|p|; +and the other, called |text(p)|, points to the |str_start| entry for +|p|'s identifier. If position~|p| of the hash table is empty, we have +|text(p)==0|; if position |p| is either empty or the end of a coalesced +hash list, we have |next(p)==0|. An auxiliary pointer variable called +|hash_used| is maintained in such a way that all locations |p >= hash_used| +are nonempty. The global variable |cs_count| tells how many multiletter +control sequences have been defined, if statistics are being kept. + +A global boolean variable called |no_new_control_sequence| is set to +|true| during the time that new hash table entries are forbidden. + +@d next(A) hash[A].lh /*link for coalesced lists*/ +@d text(A) hash[A].rh /*string number for control sequence name*/ +@d hash_is_full (hash_used==hash_base) /*test if all positions are occupied*/ +@d font_id_text(A) text(font_id_base+A) /*a frozen font identifier's name*/ + +@<Glob...@>= +static two_halves @!hash0[undefined_control_sequence-hash_base], + *const @!hash = @!hash0-hash_base; + /*the hash table*/ +static pointer @!hash_used; /*allocation pointer for |hash|*/ +static bool @!no_new_control_sequence; /*are new identifiers legal?*/ +static int @!cs_count; /*total number of known identifiers*/ + +@ @<Set init...@>= +no_new_control_sequence=true; /*new identifiers are usually forbidden*/ +next(hash_base)=0;text(hash_base)=0; +for (k=hash_base+1; k<=undefined_control_sequence-1; k++) hash[k]=hash[hash_base]; + +@ @<Initialize table entries...@>= +hash_used=frozen_control_sequence; /*nothing is used*/ +cs_count=0; +eq_type(frozen_dont_expand)=dont_expand; +text(frozen_dont_expand)=s_no("notexpanded:"); +@.notexpanded:@> + +@ Here is the subroutine that searches the hash table for an identifier +that matches a given string of length |l > 1| appearing in |buffer[j dotdot +(j+l-1)]|. If the identifier is found, the corresponding hash table address +is returned. Otherwise, if the global variable |no_new_control_sequence| +is |true|, the dummy address |undefined_control_sequence| is returned. +Otherwise the identifier is inserted into the hash table and its location +is returned. + +@p static pointer id_lookup(int @!j, int @!l) /*search the hash table*/ +{@+ /*go here if you found it*/ +int h; /*hash code*/ +int @!d; /*number of characters in incomplete current string*/ +pointer @!p; /*index in |hash| array*/ +int @!k; /*index in |buffer| array*/ +@<Compute the hash code |h|@>; +p=h+hash_base; /*we start searching here; note that |0 <= h < hash_prime|*/ +loop@+{@+if (text(p) > 0) if (length(text(p))==l) + if (str_eq_buf(text(p), j)) goto found; + if (next(p)==0) + {@+if (no_new_control_sequence) + p=undefined_control_sequence; + else@<Insert a new control sequence after |p|, then make |p| point to +it@>; + goto found; + } + p=next(p); + } +found: return p; +} + +@ @<Insert a new control...@>= +{@+if (text(p) > 0) + {@+@/do@+{if (hash_is_full) overflow("hash size", hash_size); +@:TeX capacity exceeded hash size}{\quad hash size@> + decr(hash_used); + }@+ while (!(text(hash_used)==0)); /*search for an empty location in |hash|*/ + next(p)=hash_used;p=hash_used; + } +str_room(l);d=cur_length; +while (pool_ptr > str_start[str_ptr]) + {@+decr(pool_ptr);str_pool[pool_ptr+l]=str_pool[pool_ptr]; + } /*move current string up to make room for another*/ +for (k=j; k<=j+l-1; k++) append_char(buffer[k]); +text(p)=make_string();pool_ptr=pool_ptr+d; +#ifdef @!STAT +incr(cs_count); +#endif +@;@/ +} + +@ 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; k<=j+l-1; k++) + {@+h=h+h+buffer[k]; + while (h >= hash_prime) h=h-hash_prime; + } + +@ Single-character control sequences do not need to be looked up in a hash +table, since we can use the character code itself as a direct address. +The procedure |print_cs| prints the name of a control sequence, given +a pointer to its address in |eqtb|. A space is printed after the name +unless it is a single nonletter or an active character. This procedure +might be invoked with invalid data, so it is ``extra robust.'' The +individual characters must be printed one at a time using |print|, since +they may be unprintable. + +@<Basic printing...@>= +static void print_cs(int @!p) /*prints a purported control sequence*/ +{@+if (p < hash_base) /*single character*/ + if (p >= single_base) + if (p==null_cs) + {@+print_esc("csname");print_esc("endcsname");print_char(' '); + } + else{@+printn_esc(p-single_base); + if (cat_code(p-single_base)==letter) print_char(' '); + } + else if (p < active_base) print_esc("IMPOSSIBLE."); +@.IMPOSSIBLE@> + else printn(p-active_base); +else if (p >= undefined_control_sequence) print_esc("IMPOSSIBLE."); +else if ((text(p) < 0)||(text(p) >= str_ptr)) print_esc("NONEXISTENT."); +@.NONEXISTENT@> +else{@+if (p==frozen_primitive) print_esc("primitive"); + printn_esc(text(p));print_char(' '); + } +} + +@ Here is a similar procedure; it avoids the error checks, and it never +prints a space after the control sequence. + +@<Basic printing procedures@>= +static void sprint_cs(pointer @!p) /*prints a control sequence*/ +{@+if (p < hash_base) + if (p < single_base) printn(p-active_base); + else if (p < null_cs) printn_esc(p-single_base); + else{@+print_esc("csname");print_esc("endcsname"); + } +else printn_esc(text(p)); +} + +@ We need to put \TeX's ``primitive'' control sequences into the hash +table, together with their command code (which will be the |eq_type|) +and an operand (which will be the |equiv|). The |primitive| procedure +does this, in a way that no \TeX\ user can. The global value |cur_val| +contains the new |eqtb| pointer after |primitive| has acted. + +@p +#ifdef @!INIT +static void primitive(char *@!str, quarterword @!c, halfword @!o) +{@+str_number s=s_no(str); +int k; /*index into |str_pool|*/ +int @!j; /*index into |buffer|*/ +small_number @!l; /*length of the string*/ +pointer @!p; /*pointer in |ROM|*/ +if (s < 256) cur_val=s+single_base; +else{@+k=str_start[s];l=str_start[s+1]-k; + /*we will move |s| into the (possibly non-empty) |buffer|*/ + if (first+l > buf_size+1) + overflow("buffer size", buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> + for (j=0; j<=l-1; j++) buffer[first+j]=so(str_pool[k+j]); + cur_val=id_lookup(first, l); /*|no_new_control_sequence| is |false|*/ + flush_string;text(cur_val)=s; /*we don't want to have the string twice*/ + } +eq_level(cur_val)=level_one;eq_type(cur_val)=c;equiv(cur_val)=o; +@<Add primitive definition to the |ROM| array@>; +} +#endif + +@ Many of \TeX's primitives need no |equiv|, since they are identifiable +by their |eq_type| alone. These primitives are loaded into the hash table +as follows: + +@<Put each of \TeX's primitives into the hash table@>= +primitive(" ", ex_space, 0);@/ +@!@:Single-character primitives /}{\quad\.{\\\ }@> +primitive("/", ital_corr, 0);@/ +@!@:Single-character primitives /}{\quad\.{\\/}@> +primitive("accent", accent, 0);@/ +@!@:accent\_}{\.{\\accent} primitive@> +primitive("advance", advance, 0);@/ +@!@:advance\_}{\.{\\advance} primitive@> +primitive("afterassignment", after_assignment, 0);@/ +@!@:after\_assignment\_}{\.{\\afterassignment} primitive@> +primitive("aftergroup", after_group, 0);@/ +@!@:after\_group\_}{\.{\\aftergroup} primitive@> +primitive("begingroup", begin_group, 0);@/ +@!@:begin\_group\_}{\.{\\begingroup} primitive@> +primitive("char", char_num, 0);@/ +@!@:char\_}{\.{\\char} primitive@> +primitive("csname", cs_name, 0);@/ +@!@:cs\_name\_}{\.{\\csname} primitive@> +primitive("delimiter", delim_num, 0);@/ +@!@:delimiter\_}{\.{\\delimiter} primitive@> +primitive("divide", divide, 0);@/ +@!@:divide\_}{\.{\\divide} primitive@> +primitive("endcsname", end_cs_name, 0);@/ +@!@:end\_cs\_name\_}{\.{\\endcsname} primitive@> +primitive("endgroup", end_group, 0); +@!@:end\_group\_}{\.{\\endgroup} primitive@> +text(frozen_end_group)=text(cur_val);eqtb[frozen_end_group]=eqtb[cur_val];@/ +primitive("expandafter", expand_after, 0);@/ +@!@:expand\_after\_}{\.{\\expandafter} primitive@> +primitive("font", def_font, 0);@/ +@!@:font\_}{\.{\\font} primitive@> +primitive("fontdimen", assign_font_dimen, 0);@/ +@!@:font\_dimen\_}{\.{\\fontdimen} primitive@> +primitive("halign", halign, 0);@/ +@!@:halign\_}{\.{\\halign} primitive@> +primitive("hrule", hrule, 0);@/ +@!@:hrule\_}{\.{\\hrule} primitive@> +primitive("ignorespaces", ignore_spaces, 0);@/ +@!@:ignore\_spaces\_}{\.{\\ignorespaces} primitive@> +primitive("insert", insert, 0);@/ +@!@:insert\_}{\.{\\insert} primitive@> +primitive("mark", mark, 0);@/ +@!@:mark\_}{\.{\\mark} primitive@> +primitive("mathaccent", math_accent, 0);@/ +@!@:math\_accent\_}{\.{\\mathaccent} primitive@> +primitive("mathchar", math_char_num, 0);@/ +@!@:math\_char\_}{\.{\\mathchar} primitive@> +primitive("mathchoice", math_choice, 0);@/ +@!@:math\_choice\_}{\.{\\mathchoice} primitive@> +primitive("multiply", multiply, 0);@/ +@!@:multiply\_}{\.{\\multiply} primitive@> +primitive("noalign", no_align, 0);@/ +@!@:no\_align\_}{\.{\\noalign} primitive@> +primitive("noboundary", no_boundary, 0);@/ +@!@:no\_boundary\_}{\.{\\noboundary} primitive@> +primitive("noexpand", no_expand, 0);@/ +@!@:no\_expand\_}{\.{\\noexpand} primitive@> +primitive("nonscript", non_script, 0);@/ +@!@:non\_script\_}{\.{\\nonscript} primitive@> +primitive("omit", omit, 0);@/ +@!@:omit\_}{\.{\\omit} primitive@> +primitive("parshape", set_shape, par_shape_loc);@/ +@!@:par\_shape\_}{\.{\\parshape} primitive@> +primitive("penalty", break_penalty, 0);@/ +@!@:penalty\_}{\.{\\penalty} primitive@> +primitive("prevgraf", set_prev_graf, 0);@/ +@!@:prev\_graf\_}{\.{\\prevgraf} primitive@> +primitive("radical", radical, 0);@/ +@!@:radical\_}{\.{\\radical} primitive@> +primitive("read", read_to_cs, 0);@/ +@!@:read\_}{\.{\\read} primitive@> +primitive("relax", relax, 256); /*cf.\ |scan_file_name|*/ +@!@:relax\_}{\.{\\relax} primitive@> +text(frozen_relax)=text(cur_val);eqtb[frozen_relax]=eqtb[cur_val];@/ +primitive("setbox", set_box, 0);@/ +@!@:set\_box\_}{\.{\\setbox} primitive@> +primitive("the", the, 0);@/ +@!@:the\_}{\.{\\the} primitive@> +primitive("toks", toks_register, mem_bot);@/ +@!@:toks\_}{\.{\\toks} primitive@> +primitive("vadjust", vadjust, 0);@/ +@!@:vadjust\_}{\.{\\vadjust} primitive@> +primitive("valign", valign, 0);@/ +@!@:valign\_}{\.{\\valign} primitive@> +primitive("vcenter", vcenter, 0);@/ +@!@:vcenter\_}{\.{\\vcenter} primitive@> +primitive("vrule", vrule, 0);@/ +@!@:vrule\_}{\.{\\vrule} primitive@> + +@ Each primitive has a corresponding inverse, so that it is possible to +display the cryptic numeric contents of |eqtb| in symbolic form. +Every call of |primitive| in this program is therefore accompanied by some +straightforward code that forms part of the |print_cmd_chr| routine +below. + +@<Cases of |print_cmd_chr|...@>= +case accent: print_esc("accent");@+break; +case advance: print_esc("advance");@+break; +case after_assignment: print_esc("afterassignment");@+break; +case after_group: print_esc("aftergroup");@+break; +case assign_font_dimen: print_esc("fontdimen");@+break; +case begin_group: print_esc("begingroup");@+break; +case break_penalty: print_esc("penalty");@+break; +case char_num: print_esc("char");@+break; +case cs_name: print_esc("csname");@+break; +case def_font: print_esc("font");@+break; +case delim_num: print_esc("delimiter");@+break; +case divide: print_esc("divide");@+break; +case end_cs_name: print_esc("endcsname");@+break; +case end_group: print_esc("endgroup");@+break; +case ex_space: print_esc(" ");@+break; +case expand_after: switch (chr_code) { +case 0: print_esc("expandafter");@+break; +@/@<Cases of |expandafter| for |print_cmd_chr|@>@/ +} @+break; /*there are no other cases*/ +case halign: print_esc("halign");@+break; +case hrule: print_esc("hrule");@+break; +case ignore_spaces: print_esc("ignorespaces");@+break; +case insert: print_esc("insert");@+break; +case ital_corr: print_esc("/");@+break; +case mark: {@+print_esc("mark"); + if (chr_code > 0) print_char('s'); + } @+break; +case math_accent: print_esc("mathaccent");@+break; +case math_char_num: print_esc("mathchar");@+break; +case math_choice: print_esc("mathchoice");@+break; +case multiply: print_esc("multiply");@+break; +case no_align: print_esc("noalign");@+break; +case no_boundary: print_esc("noboundary");@+break; +case no_expand: print_esc("noexpand");@+break; +case non_script: print_esc("nonscript");@+break; +case omit: print_esc("omit");@+break; +case radical: print_esc("radical");@+break; +case read_to_cs: if (chr_code==0) print_esc("read") + @<Cases of |read| for |print_cmd_chr|@>;@+break; +case relax: print_esc("relax");@+break; +case set_box: print_esc("setbox");@+break; +case set_prev_graf: print_esc("prevgraf");@+break; +case set_shape: switch (chr_code) { + case par_shape_loc: print_esc("parshape");@+break; + @<Cases of |set_shape| for |print_cmd_chr|@>@;@/ + } @+break; /*there are no other cases*/ +case the: if (chr_code==0) print_esc("the") + @<Cases of |the| for |print_cmd_chr|@>;@+break; +case toks_register: @<Cases of |toks_register| for |print_cmd_chr|@>@;@+break; +case vadjust: print_esc("vadjust");@+break; +case valign: print_esc("valign");@+break; +case vcenter: print_esc("vcenter");@+break; +case vrule: print_esc("vrule");@+break; + +@ We will deal with the other primitives later, at some point in the program +where their |eq_type| and |equiv| values are more meaningful. For example, +the primitives for math mode will be loaded when we consider the routines +that deal with formulas. It is easy to find where each particular +primitive was treated by looking in the index at the end; for example, the +section where |"radical"| entered |eqtb| is listed under `\.{\\radical} +primitive'. (Primitives consisting of a single nonalphabetic character, +@!like `\.{\\/}', are listed under `Single-character primitives'.) +@!@^Single-character primitives@> + +Meanwhile, this is a convenient place to catch up on something we were unable +to do before the hash table was defined: + +@<Print the font identifier for |font(p)|@>= +printn_esc(font_id_text(font(p))) + +@* Saving and restoring equivalents. +The nested structure provided by `$\.{\char'173}\ldots\.{\char'175}$' groups +in \TeX\ means that |eqtb| entries valid in outer groups should be saved +and restored later if they are overridden inside the braces. When a new |eqtb| +value is being assigned, the program therefore checks to see if the previous +entry belongs to an outer level. In such a case, the old value is placed +on the |save_stack| just before the new value enters |eqtb|. At the +end of a grouping level, i.e., when the right brace is sensed, the +|save_stack| is used to restore the outer values, and the inner ones are +destroyed. + +Entries on the |save_stack| are of type |memory_word|. The top item on +this stack is |save_stack[p]|, where |p==save_ptr-1|; it contains three +fields called |save_type|, |save_level|, and |save_index|, and it is +interpreted in one of five ways: + +\yskip\hangg 1) If |save_type(p)==restore_old_value|, then +|save_index(p)| is a location in |eqtb| whose current value should +be destroyed at the end of the current group and replaced by |save_stack[p-1]|. +Furthermore if |save_index(p) >= int_base|, then |save_level(p)| +should replace the corresponding entry in |xeq_level|. + +\yskip\hangg 2) If |save_type(p)==restore_zero|, then |save_index(p)| +is a location in |eqtb| whose current value should be destroyed at the end +of the current group, when it should be +replaced by the value of |eqtb[undefined_control_sequence]|. + +\yskip\hangg 3) If |save_type(p)==insert_token|, then |save_index(p)| +is a token that should be inserted into \TeX's input when the current +group ends. + +\yskip\hangg 4) If |save_type(p)==level_boundary|, then |save_level(p)| +is a code explaining what kind of group we were previously in, and +|save_index(p)| points to the level boundary word at the bottom of +the entries for that group. +Furthermore, in extended \eTeX\ mode, |save_stack[p-1]| contains the +source line number at which the current level of grouping was entered. + +\yskip\hang 5) If |save_type(p)==restore_sa|, then |sa_chain| points to a +chain of sparse array entries to be restored at the end of the current +group. Furthermore |save_index(p)| and |save_level(p)| should replace +the values of |sa_chain| and |sa_level| respectively. + +@d save_type(A) save_stack[A].hh.b0 /*classifies a |save_stack| entry*/ +@d save_level(A) save_stack[A].hh.b1 + /*saved level for regions 5 and 6, or group code*/ +@d save_index(A) save_stack[A].hh.rh + /*|eqtb| location or token or |save_stack| location*/ +@d restore_old_value 0 /*|save_type| when a value should be restored later*/ +@d restore_zero 1 /*|save_type| when an undefined entry should be restored*/ +@d insert_token 2 /*|save_type| when a token is being saved for later use*/ +@d level_boundary 3 /*|save_type| corresponding to beginning of group*/ +@d restore_sa 4 /*|save_type| when sparse array entries should be restored*/ + +@p @t\4@>@<Declare \eTeX\ procedures for tracing and input@>@; + +@ Here are the group codes that are used to discriminate between different +kinds of groups. They allow \TeX\ to decide what special actions, if any, +should be performed when a group ends. +\def\grp{\.{\char'173...\char'175}} + +Some groups are not supposed to be ended by right braces. For example, +the `\.\$' that begins a math formula causes a |math_shift_group| to +be started, and this should be terminated by a matching `\.\$'. Similarly, +a group that starts with \.{\\left} should end with \.{\\right}, and +one that starts with \.{\\begingroup} should end with \.{\\endgroup}. + +@d bottom_level 0 /*group code for the outside world*/ +@d simple_group 1 /*group code for local structure only*/ +@d hbox_group 2 /*code for `\.{\\hbox}\grp'*/ +@d adjusted_hbox_group 3 /*code for `\.{\\hbox}\grp' in vertical mode*/ +@d vbox_group 4 /*code for `\.{\\vbox}\grp'*/ +@d vtop_group 5 /*code for `\.{\\vtop}\grp'*/ +@d align_group 6 /*code for `\.{\\halign}\grp', `\.{\\valign}\grp'*/ +@d no_align_group 7 /*code for `\.{\\noalign}\grp'*/ +@d output_group 8 /*code for output routine*/ +@d math_group 9 /*code for, e.g., `\.{\char'136}\grp'*/ +@d disc_group 10 /*code for `\.{\\discretionary}\grp\grp\grp'*/ +@d insert_group 11 /*code for `\.{\\insert}\grp', `\.{\\vadjust}\grp'*/ +@d vcenter_group 12 /*code for `\.{\\vcenter}\grp'*/ +@d math_choice_group 13 /*code for `\.{\\mathchoice}\grp\grp\grp\grp'*/ +@d semi_simple_group 14 /*code for `\.{\\begingroup...\\endgroup}'*/ +@d math_shift_group 15 /*code for `\.{\$...\$}'*/ +@d math_left_group 16 /*code for `\.{\\left...\\right}'*/ +@d max_group_code 16 + +@<Types...@>= +typedef int8_t group_code; /*|save_level| for a level boundary*/ + +@ The global variable |cur_group| keeps track of what sort of group we are +currently in. Another global variable, |cur_boundary|, points to the +topmost |level_boundary| word. And |cur_level| is the current depth of +nesting. The routines are designed to preserve the condition that no entry +in the |save_stack| or in |eqtb| ever has a level greater than |cur_level|. + +@ @<Glob...@>= +static memory_word @!save_stack[save_size+1]; +static int @!save_ptr; /*first unused entry on |save_stack|*/ +static int @!max_save_stack; /*maximum usage of save stack*/ +static quarterword @!cur_level; /*current nesting level for groups*/ +static group_code @!cur_group; /*current group type*/ +static int @!cur_boundary; /*where the current level begins*/ + +@ At this time it might be a good idea for the reader to review the introduction +to |eqtb| that was given above just before the long lists of parameter names. +Recall that the ``outer level'' of the program is |level_one|, since +undefined control sequences are assumed to be ``defined'' at |level_zero|. + +@<Set init...@>= +save_ptr=0;cur_level=level_one;cur_group=bottom_level;cur_boundary=0; +max_save_stack=0; + +@ The following macro is used to test if there is room for up to seven more +entries on |save_stack|. By making a conservative test like this, we can +get by with testing for overflow in only a few places. + +@d check_full_save_stack if (save_ptr > max_save_stack) + {@+max_save_stack=save_ptr; + if (max_save_stack > save_size-7) overflow("save size", save_size); +@:TeX capacity exceeded save size}{\quad save size@> + } + +@ Procedure |new_save_level| is called when a group begins. The +argument is a group identification code like `|hbox_group|'. After +calling this routine, it is safe to put five more entries on |save_stack|. + +In some cases integer-valued items are placed onto the +|save_stack| just below a |level_boundary| word, because this is a +convenient place to keep information that is supposed to ``pop up'' just +when the group has finished. +For example, when `\.{\\hbox to 100pt}\grp' is being treated, the 100pt +dimension is stored on |save_stack| just before |new_save_level| is +called. + +We use the notation |saved(k)| to stand for an integer item that +appears in location |save_ptr+k| of the save stack. + +@d saved(A) save_stack[save_ptr+A].i + +@p static void new_save_level(group_code @!c) /*begin a new level of grouping*/ +{@+check_full_save_stack; +if (eTeX_ex) + {@+saved(0)=line;incr(save_ptr); + } +save_type(save_ptr)=level_boundary;save_level(save_ptr)=cur_group; +save_index(save_ptr)=cur_boundary; +if (cur_level==max_quarterword) overflow("grouping levels", +@:TeX capacity exceeded grouping levels}{\quad grouping levels@> + max_quarterword-min_quarterword); + /*quit if |(cur_level+1)| is too big to be stored in |eqtb|*/ +cur_boundary=save_ptr;cur_group=c; +#ifdef @!STAT +if (tracing_groups > 0) group_trace(false); +#endif +@;@/ +incr(cur_level);incr(save_ptr); +} + +@ Just before an entry of |eqtb| is changed, the following procedure should +be called to update the other data structures properly. It is important +to keep in mind that reference counts in |mem| include references from +within |save_stack|, so these counts must be handled carefully. +@^reference counts@> + +@p static void eq_destroy(memory_word @!w) /*gets ready to forget |w|*/ +{@+pointer q; /*|equiv| field of |w|*/ +switch (eq_type_field(w)) { +case call: case long_call: case outer_call: + case long_outer_call: delete_token_ref(equiv_field(w));@+break; +case glue_ref: delete_glue_ref(equiv_field(w));@+break; +case shape_ref: {@+q=equiv_field(w); /*we need to free a \.{\\parshape} block*/ + if (q!=null) free_node(q, info(q)+info(q)+1); + } @+break; /*such a block is |2 n+1| words long, where |n==info(q)|*/ +case box_ref: flush_node_list(equiv_field(w));@+break; +@/@<Cases for |eq_destroy|@>@/ +default:do_nothing; +} +} + +@ To save a value of |eqtb[p]| that was established at level |l|, we +can use the following subroutine. + +@p static void eq_save(pointer @!p, quarterword @!l) /*saves |eqtb[p]|*/ +{@+check_full_save_stack; +if (l==level_zero) save_type(save_ptr)=restore_zero; +else{@+save_stack[save_ptr]=eqtb[p];incr(save_ptr); + save_type(save_ptr)=restore_old_value; + } +save_level(save_ptr)=l;save_index(save_ptr)=p;incr(save_ptr); +} + +@ The procedure |eq_define| defines an |eqtb| entry having specified +|eq_type| and |equiv| fields, and saves the former value if appropriate. +This procedure is used only for entries in the first four regions of |eqtb|, +i.e., only for entries that have |eq_type| and |equiv| fields. +After calling this routine, it is safe to put four more entries on +|save_stack|, provided that there was room for four more entries before +the call, since |eq_save| makes the necessary test. + +@p +#ifdef @!STAT +#define assign_trace(A, B) if (tracing_assigns > 0) restore_trace(A, B); +#else +#define assign_trace(A, B) +#endif + +static void eq_define(pointer @!p, quarterword @!t, halfword @!e) + /*new data for |eqtb|*/ +{@+ +if (eTeX_ex&&(eq_type(p)==t)&&(equiv(p)==e)) + {@+assign_trace(p,"reassigning")@;@/ + eq_destroy(eqtb[p]);return; + } +assign_trace(p,"changing")@;@/ +if (eq_level(p)==cur_level) eq_destroy(eqtb[p]); +else if (cur_level > level_one) eq_save(p, eq_level(p)); +eq_level(p)=cur_level;eq_type(p)=t;equiv(p)=e; +assign_trace(p,"into")@;@/ +} + +@ The counterpart of |eq_define| for the remaining (fullword) positions in +|eqtb| is called |eq_word_define|. Since |xeq_level[p] >= level_one| for all +|p|, a `|restore_zero|' will never be used in this case. + +@p static void eq_word_define(pointer @!p, int @!w) +{@+ +if (eTeX_ex&&(eqtb[p].i==w)) + {@+assign_trace(p,"reassigning")@;@/ + return; + } +assign_trace(p,"changing")@;@/ +if (xeq_level[p]!=cur_level) + {@+eq_save(p, xeq_level[p]);xeq_level[p]=cur_level; + } +eqtb[p].i=w; +assign_trace(p,"into")@;@/ +} + +@ The |eq_define| and |eq_word_define| routines take care of local definitions. +@^global definitions@> +Global definitions are done in almost the same way, but there is no need +to save old values, and the new value is associated with |level_one|. + +@p static void geq_define(pointer @!p, quarterword @!t, halfword @!e) + /*global |eq_define|*/ +{@+assign_trace(p,"globally changing")@;@/ +{@+eq_destroy(eqtb[p]); +eq_level(p)=level_one;eq_type(p)=t;equiv(p)=e; +} +assign_trace(p,"into");@/ +} +@# +static void geq_word_define(pointer @!p, int @!w) /*global |eq_word_define|*/ +{@+assign_trace(p,"globally changing")@;@/ +{@+eqtb[p].i=w;xeq_level[p]=level_one; +} +assign_trace(p,"into");@/ +} + +@ Subroutine |save_for_after| puts a token on the stack for save-keeping. + +@p static void save_for_after(halfword @!t) +{@+if (cur_level > level_one) + {@+check_full_save_stack; + save_type(save_ptr)=insert_token;save_level(save_ptr)=level_zero; + save_index(save_ptr)=t;incr(save_ptr); + } +} + +@ The |unsave| routine goes the other way, taking items off of |save_stack|. +This routine takes care of restoration when a level ends; everything +belonging to the topmost group is cleared off of the save stack. + +@p +static void back_input(void); +static void unsave(void) /*pops the top level off the save stack*/ +{@+ +pointer p; /*position to be restored*/ +quarterword @!l; /*saved level, if in fullword regions of |eqtb|*/ +halfword @!t; /*saved value of |cur_tok|*/ +bool @!a; /*have we already processed an \.{\\aftergroup} ?*/ +a=false; +if (cur_level > level_one) + {@+decr(cur_level); + @<Clear off top level from |save_stack|@>; + } +else confusion("curlevel"); /*|unsave| is not used when |cur_group==bottom_level|*/ +@:this can't happen curlevel}{\quad curlevel@> +} + +@ @<Clear off...@>= +loop@+{@+decr(save_ptr); + if (save_type(save_ptr)==level_boundary) goto done; + p=save_index(save_ptr); + if (save_type(save_ptr)==insert_token) + @<Insert token |p| into \TeX's input@>@; + else if (save_type(save_ptr)==restore_sa) + {@+sa_restore();sa_chain=p;sa_level=save_level(save_ptr); + } + else{@+if (save_type(save_ptr)==restore_old_value) + {@+l=save_level(save_ptr);decr(save_ptr); + } + else save_stack[save_ptr]=eqtb[undefined_control_sequence]; + @<Store \(s)|save_stack[save_ptr]| in |eqtb[p]|, unless |eqtb[p]| holds +a global value@>; + } + } +done: +#ifdef @!STAT +if (tracing_groups > 0) group_trace(true); +#endif +@;@/ +if (grp_stack[in_open]==cur_boundary) group_warning(); + /*groups possibly not properly nested with files*/ +cur_group=save_level(save_ptr);cur_boundary=save_index(save_ptr); +if (eTeX_ex) decr(save_ptr) + +@ A global definition, which sets the level to |level_one|, +@^global definitions@> +will not be undone by |unsave|. If at least one global definition of +|eqtb[p]| has been carried out within the group that just ended, the +last such definition will therefore survive. + +@<Store \(s)|save...@>= +if (p < int_base) + if (eq_level(p)==level_one) + {@+eq_destroy(save_stack[save_ptr]); /*destroy the saved value*/ +#ifdef @!STAT + if (tracing_restores > 0) restore_trace(p,"retaining"); +#endif +@;@/ + } + else{@+eq_destroy(eqtb[p]); /*destroy the current value*/ + eqtb[p]=save_stack[save_ptr]; /*restore the saved value*/ +#ifdef @!STAT + if (tracing_restores > 0) restore_trace(p,"restoring"); +#endif +@;@/ + } +else if (xeq_level[p]!=level_one) + {@+eqtb[p]=save_stack[save_ptr];xeq_level[p]=l; +#ifdef @!STAT + if (tracing_restores > 0) restore_trace(p,"restoring"); +#endif +@;@/ + } +else{ +#ifdef @!STAT + if (tracing_restores > 0) restore_trace(p,"retaining"); +#endif +@;@/ + } + +@ @<Declare \eTeX\ procedures for tr...@>= +#ifdef @!STAT +static void restore_trace(pointer @!p, char *@!s) + /*|eqtb[p]| has just been restored or retained*/ +{@+begin_diagnostic();print_char('{');print(s);print_char(' '); +show_eqtb(p);print_char('}'); +end_diagnostic(false); +} +#endif + +@ When looking for possible pointers to a memory location, it is helpful +to look for references from |eqtb| that might be waiting on the +save stack. Of course, we might find spurious pointers too; but this +routine is merely an aid when debugging, and at such times we are +grateful for any scraps of information, even if they prove to be irrelevant. +@^dirty \PASCAL@> + +@<Search |save_stack| for equivalents that point to |p|@>= +if (save_ptr > 0) for (q=0; q<=save_ptr-1; q++) + {@+if (equiv_field(save_stack[q])==p) + {@+print_nl("SAVE(");print_int(q);print_char(')'); + } + } + +@ Most of the parameters kept in |eqtb| can be changed freely, but there's +an exception: The magnification should not be used with two different +values during any \TeX\ job, since a single magnification is applied to an +entire run. The global variable |mag_set| is set to the current magnification +whenever it becomes necessary to ``freeze'' it at a particular value. + +@<Glob...@>= +static int @!mag_set; /*if nonzero, this magnification should be used henceforth*/ + +@ @<Set init...@>= +mag_set=0; + +@ The |prepare_mag| subroutine is called whenever \TeX\ wants to use |mag| +for magnification. + +@p static void prepare_mag(void) +{@+if ((mag_set > 0)&&(mag!=mag_set)) + {@+print_err("Incompatible magnification (");print_int(mag); +@.Incompatible magnification@> + print(");");print_nl(" the previous value will be retained"); + help2("I can handle only one magnification ratio per job. So I've",@/ + "reverted to the magnification you used earlier on this run.");@/ + int_error(mag_set); + geq_word_define(int_base+mag_code, mag_set); /*|mag=mag_set|*/ + } +if ((mag <= 0)||(mag > 32768)) + {@+print_err("Illegal magnification has been changed to 1000");@/ +@.Illegal magnification...@> + help1("The magnification ratio must be between 1 and 32768."); + int_error(mag);geq_word_define(int_base+mag_code, 1000); + } +mag_set=mag; +} + +@* Token lists. +A \TeX\ token is either a character or a control sequence, and it is +@^token@> +represented internally in one of two ways: (1)~A character whose ASCII +code number is |c| and whose command code is |m| is represented as the +number $2^8m+c$; the command code is in the range |1 <= m <= 14|. (2)~A control +sequence whose |eqtb| address is |p| is represented as the number +|cs_token_flag+p|. Here |cs_token_flag==@t$2^{12}-1$@>| is larger than +$2^8m+c$, yet it is small enough that |cs_token_flag+p < max_halfword|; +thus, a token fits comfortably in a halfword. + +A token |t| represents a |left_brace| command if and only if +|t < left_brace_limit|; it represents a |right_brace| command if and only if +we have |left_brace_limit <= t < right_brace_limit|; and it represents a |match| or +|end_match| command if and only if |match_token <= t <= end_match_token|. +The following definitions take care of these token-oriented constants +and a few others. + +@d cs_token_flag 07777 /*amount added to the |eqtb| location in a + token that stands for a control sequence; is a multiple of~256, less~1*/ +@d left_brace_token 00400 /*$2^8\cdot|left_brace|$*/ +@d left_brace_limit 01000 /*$2^8\cdot(|left_brace|+1)$*/ +@d right_brace_token 01000 /*$2^8\cdot|right_brace|$*/ +@d right_brace_limit 01400 /*$2^8\cdot(|right_brace|+1)$*/ +@d math_shift_token 01400 /*$2^8\cdot|math_shift|$*/ +@d tab_token 02000 /*$2^8\cdot|tab_mark|$*/ +@d out_param_token 02400 /*$2^8\cdot|out_param|$*/ +@d space_token 05040 /*$2^8\cdot|spacer|+|' '|$*/ +@d letter_token 05400 /*$2^8\cdot|letter|$*/ +@d other_token 06000 /*$2^8\cdot|other_char|$*/ +@d match_token 06400 /*$2^8\cdot|match|$*/ +@d end_match_token 07000 /*$2^8\cdot|end_match|$*/ +@d protected_token 07001 /*$2^8\cdot|end_match|+1$*/ + +@ @<Check the ``constant''...@>= +if (cs_token_flag+undefined_control_sequence > max_halfword) bad=21; + +@ A token list is a singly linked list of one-word nodes in |mem|, where +each word contains a token and a link. Macro definitions, output-routine +definitions, marks, \.{\\write} texts, and a few other things +are remembered by \TeX\ in the form +of token lists, usually preceded by a node with a reference count in its +|token_ref_count| field. The token stored in location |p| is called +|info(p)|. + +Three special commands appear in the token lists of macro definitions. +When |m==match|, it means that \TeX\ should scan a parameter +for the current macro; when |m==end_match|, it means that parameter +matching should end and \TeX\ should start reading the macro text; and +when |m==out_param|, it means that \TeX\ should insert parameter +number |c| into the text at this point. + +The enclosing \.{\char'173} and \.{\char'175} characters of a macro +definition are omitted, but an output routine +will be enclosed in braces. + +Here is an example macro definition that illustrates these conventions. +After \TeX\ processes the text +$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$ +the definition of \.{\\mac} is represented as a token list containing +$$\def\,{\hskip2pt} +\vbox{\halign{\hfil#\hfil\cr +(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ , +\.{\\b}, |end_match|,\cr +|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#, +|other_char|\,\.1,\cr +|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$ +The procedure |scan_toks| builds such token lists, and |macro_call| +does the parameter matching. +@^reference counts@> + +Examples such as +$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ +explain why reference counts would be needed even if \TeX\ had no \.{\\let} +operation: When the token list for \.{\\m} is being read, the redefinition of +\.{\\m} changes the |eqtb| entry before the token list has been fully +consumed, so we dare not simply destroy a token list when its +control sequence is being redefined. + +If the parameter-matching part of a definition ends with `\.{\#\{}', +the corresponding token list will have `\.\{' just before the `|end_match|' +and also at the very end. The first `\.\{' is used to delimit the parameter; the +second one keeps the first from disappearing. + +@ The procedure |show_token_list|, which prints a symbolic form of +the token list that starts at a given node |p|, illustrates these +conventions. The token list being displayed should not begin with a reference +count. However, the procedure is intended to be robust, so that if the +memory links are awry or if |p| is not really a pointer to a token list, +nothing catastrophic will happen. + +An additional parameter |q| is also given; this parameter is either null +or it points to a node in the token list where a certain magic computation +takes place that will be explained later. (Basically, |q| is non-null when +we are printing the two-line context information at the time of an error +message; |q| marks the place corresponding to where the second line +should begin.) + +For example, if |p| points to the node containing the first \.a in the +token list above, then |show_token_list| will print the string +$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ +and if |q| points to the node containing the second \.a, +the magic computation will be performed just before the second \.a is printed. + +The generation will stop, and `\.{\\ETC.}' will be printed, if the length +of printing exceeds a given limit~|l|. Anomalous entries are printed in the +form of control sequences that are not followed by a blank space, e.g., +`\.{\\BAD.}'; this cannot be confused with actual control sequences because +a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'. + +@<Declare the procedure called |show_token_list|@>= +static void show_token_list(int @!p, int @!q, int @!l) +{@+ +int m, @!c; /*pieces of a token*/ +ASCII_code @!match_chr; /*character used in a `|match|'*/ +ASCII_code @!n; /*the highest parameter number, as an ASCII digit*/ +match_chr='#';n='0';tally=0; +while ((p!=null)&&(tally < l)) + {@+if (p==q) @<Do magic computation@>; + @<Display token |p|, and |return| if there are problems@>; + p=link(p); + } +if (p!=null) print_esc("ETC."); +@.ETC@> + +} + +@ @<Display token |p|...@>= +if ((p < hi_mem_min)||(p > mem_end)) + {@+print_esc("CLOBBERED.");return; +@.CLOBBERED@> + } +if (info(p) >= cs_token_flag) print_cs(info(p)-cs_token_flag); +else{@+m=info(p)/0400;c=info(p)%0400; + if (info(p) < 0) print_esc("BAD."); +@.BAD@> + else@<Display the token $(|m|,|c|)$@>; + } + +@ The procedure usually ``learns'' the character code used for macro +parameters by seeing one in a |match| command before it runs into any +|out_param| commands. + +@<Display the token...@>= +switch (m) { +case left_brace: case right_brace: case math_shift: + case tab_mark: case sup_mark: case sub_mark: + case spacer: + case letter: case other_char: printn(c);@+break; +case mac_param: {@+printn(c);printn(c); + } @+break; +case out_param: {@+printn(match_chr); + if (c <= 9) print_char(c+'0'); + else{@+print_char('!');return; + } + } @+break; +case match: {@+match_chr=c;printn(c);incr(n);print_char(n); + if (n > '9') return; + } @+break; +case end_match: if (c==0) print("->");@+break; +@.->@> +default:print_esc("BAD."); +@.BAD@> +} + +@ Here's the way we sometimes want to display a token list, given a pointer +to its reference count; the pointer may be null. + +@p static void token_show(pointer @!p) +{@+if (p!=null) show_token_list(link(p), null, 10000000); +} + +@ The |print_meaning| subroutine displays |cur_cmd| and |cur_chr| in +symbolic form, including the expansion of a macro or mark. + +@p static void print_meaning(void) +{@+print_cmd_chr(cur_cmd, cur_chr); +if (cur_cmd >= call) + {@+print_char(':');print_ln();token_show(cur_chr); + } +else if ((cur_cmd==top_bot_mark)&&(cur_chr < marks_code)) + {@+print_char(':');print_ln(); + token_show(cur_mark[cur_chr]); + } +} + +@* Introduction to the syntactic routines. +Let's pause a moment now and try to look at the Big Picture. +The \TeX\ program consists of three main parts: syntactic routines, +semantic routines, and output routines. The chief purpose of the +syntactic routines is to deliver the user's input to the semantic routines, +one token at a time. The semantic routines act as an interpreter +responding to these tokens, which may be regarded as commands. And the +output routines are periodically called on to convert box-and-glue +lists into a compact set of instructions that will be sent +to a typesetter. We have discussed the basic data structures and utility +routines of \TeX, so we are good and ready to plunge into the real activity by +considering the syntactic routines. + +Our current goal is to come to grips with the |get_next| procedure, +which is the keystone of \TeX's input mechanism. Each call of |get_next| +sets the value of three variables |cur_cmd|, |cur_chr|, and |cur_cs|, +representing the next input token. +$$\vbox{\halign{#\hfil\cr + \hbox{|cur_cmd| denotes a command code from the long list of codes + given above;}\cr + \hbox{|cur_chr| denotes a character code or other modifier of the command + code;}\cr + \hbox{|cur_cs| is the |eqtb| location of the current control sequence,}\cr + \hbox{\qquad if the current token was a control sequence, + otherwise it's zero.}\cr}}$$ +Underlying this external behavior of |get_next| is all the machinery +necessary to convert from character files to tokens. At a given time we +may be only partially finished with the reading of several files (for +which \.{\\input} was specified), and partially finished with the expansion +of some user-defined macros and/or some macro parameters, and partially +finished with the generation of some text in a template for \.{\\halign}, +and so on. When reading a character file, special characters must be +classified as math delimiters, etc.; comments and extra blank spaces must +be removed, paragraphs must be recognized, and control sequences must be +found in the hash table. Furthermore there are occasions in which the +scanning routines have looked ahead for a word like `\.{plus}' but only +part of that word was found, hence a few characters must be put back +into the input and scanned again. + +To handle these situations, which might all be present simultaneously, +\TeX\ uses various stacks that hold information about the incomplete +activities, and there is a finite state control for each level of the +input mechanism. These stacks record the current state of an implicitly +recursive process, but the |get_next| procedure is not recursive. +Therefore it will not be difficult to translate these algorithms into +low-level languages that do not support recursion. + +@<Glob...@>= +static eight_bits @!cur_cmd; /*current command set by |get_next|*/ +static halfword @!cur_chr; /*operand of current command*/ +static pointer @!cur_cs; /*control sequence found here, zero if none found*/ +static halfword @!cur_tok; /*packed representative of |cur_cmd| and |cur_chr|*/ + +@ The |print_cmd_chr| routine prints a symbolic interpretation of a +command code and its modifier. This is used in certain `\.{You can\'t}' +error messages, and in the implementation of diagnostic routines like +\.{\\show}. + +The body of |print_cmd_chr| is a rather tedious listing of print +commands, and most of it is essentially an inverse to the |primitive| +routine that enters a \TeX\ primitive into |eqtb|. Therefore much of +this procedure appears elsewhere in the program, +together with the corresponding |primitive| calls. + +@d chr_cmd(A) {@+print(A);print_ASCII(chr_code); + } + +@<Declare the procedure called |print_cmd_chr|@>= +static void print_cmd_chr(quarterword @!cmd, halfword @!chr_code) +{@+int n; /*temp variable*/ +switch (cmd) { +case left_brace: chr_cmd("begin-group character ")@;@+break; +case right_brace: chr_cmd("end-group character ")@;@+break; +case math_shift: chr_cmd("math shift character ")@;@+break; +case mac_param: chr_cmd("macro parameter character ")@;@+break; +case sup_mark: chr_cmd("superscript character ")@;@+break; +case sub_mark: chr_cmd("subscript character ")@;@+break; +case endv: print("end of alignment template");@+break; +case spacer: chr_cmd("blank space ")@;@+break; +case letter: chr_cmd("the letter ")@;@+break; +case other_char: chr_cmd("the character ")@;@+break; +@t\4@>@<Cases of |print_cmd_chr| for symbolic printing of primitives@>@/ +default:print("[unknown command code!]"); +} +} + +@ Here is a procedure that displays the current command. + +@p static void show_cur_cmd_chr(void) +{@+int n; /*level of \.{\\if...\\fi} nesting*/ +int @!l; /*line where \.{\\if} started*/ +pointer @!p; +begin_diagnostic();print_nl("{"); +if (mode!=shown_mode) + {@+print_mode(mode);print(": ");shown_mode=mode; + } +print_cmd_chr(cur_cmd, cur_chr); +if (tracing_ifs > 0) + if (cur_cmd >= if_test) if (cur_cmd <= fi_or_else) + {@+print(": "); + if (cur_cmd==fi_or_else) + {@+print_cmd_chr(if_test, cur_if);print_char(' '); + n=0;l=if_line; + } + else{@+n=1;l=line; + } + p=cond_ptr; + while (p!=null) + {@+incr(n);p=link(p); + } + print("(level ");print_int(n);print_char(')');print_if_line(l); + } +print_char('}'); +end_diagnostic(false); +} + +@* Input stacks and states. +This implementation of +\TeX\ uses two different conventions for representing sequential stacks. +@^stack conventions@>@^conventions for representing stacks@> + +\yskip\hangg 1) If there is frequent access to the top entry, and if the +stack is essentially never empty, then the top entry is kept in a global +variable (even better would be a machine register), and the other entries +appear in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the +semantic stack described above is handled this way, and so is the input +stack that we are about to study. + +\yskip\hangg 2) If there is infrequent top access, the entire stack contents +are in the array $\\{stack}[0\to(\\{ptr}-1)]$. For example, the |save_stack| +is treated this way, as we have seen. + +\yskip\noindent +The state of \TeX's input mechanism appears in the input stack, whose +entries are records with six fields, called |state|, |index|, |start|, |loc|, +|limit|, and |name|. This stack is maintained with +convention~(1), so it is declared in the following way: + +@<Types...@>= +typedef struct { + quarterword @!state_field, @!index_field; + halfword @!start_field, @!loc_field, @!limit_field, @!name_field; + halfword @!depth_field; + } in_state_record; + +@ @<Glob...@>= +static in_state_record @!input_stack[stack_size+1]; +static int @!input_ptr; /*first unused location of |input_stack|*/ +static int @!max_in_stack; /*largest value of |input_ptr| when pushing*/ +static in_state_record @!cur_input; + /*the ``top'' input state, according to convention (1)*/ + +@ We've already defined the special variable |loc==cur_input.loc_field| +in our discussion of basic input-output routines. The other components of +|cur_input| are defined in the same way: + +@d state cur_input.state_field /*current scanner state*/ +@d index cur_input.index_field /*reference for buffer information*/ +@d start cur_input.start_field /*starting position in |buffer|*/ +@d limit cur_input.limit_field /*end of current line in |buffer|*/ +@d name cur_input.name_field /*name of the current file*/ +@d cur_depth cur_input.depth_field /* nesting depth of current macro */ + +@ Let's look more closely now at the control variables +(|state|,~|index|,~|start|,~|loc|,~|limit|,~|name|), +assuming that \TeX\ is reading a line of characters that have been input +from some file or from the user's terminal. There is an array called +|buffer| that acts as a stack of all lines of characters that are +currently being read from files, including all lines on subsidiary +levels of the input stack that are not yet completed. \TeX\ will return to +the other lines when it is finished with the present input file. + +(Incidentally, on a machine with byte-oriented addressing, it might be +appropriate to combine |buffer| with the |str_pool| array, +letting the buffer entries grow downward from the top of the string pool +and checking that these two tables don't bump into each other.) + +The line we are currently working on begins in position |start| of the +buffer; the next character we are about to read is |buffer[loc]|; and +|limit| is the location of the last character present. If |loc > limit|, +the line has been completely read. Usually |buffer[limit]| is the +|end_line_char|, denoting the end of a line, but this is not +true if the current line is an insertion that was entered on the user's +terminal in response to an error message. + +The |name| variable is a string number that designates the name of +the current file, if we are reading a text file. It is zero if we +are reading from the terminal; it is |n+1| if we are reading from +input stream |n|, where |0 <= n <= 16|. (Input stream 16 stands for +an invalid stream number; in such cases the input is actually from +the terminal, under control of the procedure |read_toks|.) +Finally |18 <= name <= 19| indicates that we are reading a pseudo file +created by the \.{\\scantokens} command. + +The |state| variable has one of three values, when we are scanning such +files: +$$\baselineskip 15pt\vbox{\halign{#\hfil\cr +1) |state==mid_line| is the normal state.\cr +2) |state==skip_blanks| is like |mid_line|, but blanks are ignored.\cr +3) |state==new_line| is the state at the beginning of a line.\cr}}$$ +These state values are assigned numeric codes so that if we add the state +code to the next character's command code, we get distinct values. For +example, `|mid_line+spacer|' stands for the case that a blank +space character occurs in the middle of a line when it is not being +ignored; after this case is processed, the next value of |state| will +be |skip_blanks|. + +@d mid_line 1 /*|state| code when scanning a line of characters*/ +@d skip_blanks (2+max_char_code) /*|state| code when ignoring blanks*/ +@d new_line (3+max_char_code+max_char_code) /*|state| code at start of line*/ + +@ Additional information about the current line is available via the +|index| variable, which counts how many lines of characters are present +in the buffer below the current level. We have |index==0| when reading +from the terminal and prompting the user for each line; then if the user types, +e.g., `\.{\\input paper}', we will have |index==1| while reading +the file \.{paper.tex}. However, it does not follow that |index| is the +same as the input stack pointer, since many of the levels on the input +stack may come from token lists. For example, the instruction `\.{\\input +paper}' might occur in a token list. + +The global variable |in_open| is equal to the |index| +value of the highest non-token-list level. Thus, the number of partially read +lines in the buffer is |in_open+1|, and we have |in_open==index| +when we are not reading a token list. + +If we are not currently reading from the terminal, or from an input +stream, we are reading from the file variable |input_file[index]|. We use +the notation |terminal_input| as a convenient abbreviation for |name==0|, +and |cur_file| as an abbreviation for |input_file[index]|. + +The global variable |line| contains the line number in the topmost +open file, for use in error messages. If we are not reading from +the terminal, |line_stack[index]| holds the line number for the +enclosing level, so that |line| can be restored when the current +file has been read. Line numbers should never be negative, since the +negative of the current line number is used to identify the user's output +routine in the |mode_line| field of the semantic nest entries. + +If more information about the input state is needed, it can be +included in small arrays like those shown here. For example, +the current page or segment number in the input file might be +put into a variable |@!page|, maintained for enclosing levels in +`\ignorespaces|@!page_stack: array[1 dotdot max_in_open]int|\unskip' +by analogy with |line_stack|. +@^system dependencies@> + +@d terminal_input (name==0) /*are we reading from the terminal?*/ +@d cur_file input_file[index] /*the current |alpha_file| variable*/ + +@<Glob...@>= +static int @!in_open; /*the number of lines in the buffer, less one*/ +static int @!open_parens; /*the number of open text files*/ +static alpha_file @!input_file0[max_in_open], + *const @!input_file = @!input_file0-1; +static int @!line; /*current line number in the current source file*/ +static int @!line_stack0[max_in_open], + *const @!line_stack = @!line_stack0-1; + +@ Users of \TeX\ sometimes forget to balance left and right braces properly, +and one of the ways \TeX\ tries to spot such errors is by considering an +input file as broken into subfiles by control sequences that +are declared to be \.{\\outer}. + +A variable called |scanner_status| tells \TeX\ whether or not to complain +when a subfile ends. This variable has six possible values: + +\yskip\hang|normal|, means that a subfile can safely end here without incident. + +\yskip\hang|skipping|, means that a subfile can safely end here, but not a file, +because we're reading past some conditional text that was not selected. + +\yskip\hang|defining|, means that a subfile shouldn't end now because a +macro is being defined. + +\yskip\hang|matching|, means that a subfile shouldn't end now because a +macro is being used and we are searching for the end of its arguments. + +\yskip\hang|aligning|, means that a subfile shouldn't end now because we are +not finished with the preamble of an \.{\\halign} or \.{\\valign}. + +\yskip\hang|absorbing|, means that a subfile shouldn't end now because we are +reading a balanced token list for \.{\\message}, \.{\\write}, etc. + +\yskip\noindent +If the |scanner_status| is not |normal|, the variable |warning_index| points +to the |eqtb| location for the relevant control sequence name to print +in an error message. + +@d skipping 1 /*|scanner_status| when passing conditional text*/ +@d defining 2 /*|scanner_status| when reading a macro definition*/ +@d matching 3 /*|scanner_status| when reading macro arguments*/ +@d aligning 4 /*|scanner_status| when reading an alignment preamble*/ +@d absorbing 5 /*|scanner_status| when reading a balanced text*/ + +@<Glob...@>= +static int @!scanner_status; /*can a subfile end now?*/ +static pointer @!warning_index; /*identifier relevant to non-|normal| scanner status*/ +static pointer @!def_ref; /*reference count of token list being defined*/ + +@ Here is a procedure that uses |scanner_status| to print a warning message +when a subfile has ended, and at certain other crucial times: + +@<Declare the procedure called |runaway|@>= +static void runaway(void) +{@+pointer p; /*head of runaway list*/ +if (scanner_status > skipping) + {@+print_nl("Runaway "); +@.Runaway...@> + switch (scanner_status) { + case defining: {@+print("definition");p=def_ref; + } @+break; + case matching: {@+print("argument");p=temp_head; + } @+break; + case aligning: {@+print("preamble");p=hold_head; + } @+break; + case absorbing: {@+print("text");p=def_ref; + } + } /*there are no other cases*/ + print_char('?');print_ln();show_token_list(link(p), null, error_line-10); + } +} + +@ However, all this discussion about input state really applies only to the +case that we are inputting from a file. There is another important case, +namely when we are currently getting input from a token list. In this case +|state==token_list|, and the conventions about the other state variables +are different: + +\yskip\hang|loc| is a pointer to the current node in the token list, i.e., +the node that will be read next. If |loc==null|, the token list has been +fully read. + +\yskip\hang|start| points to the first node of the token list; this node +may or may not contain a reference count, depending on the type of token +list involved. + +\yskip\hang|token_type|, which takes the place of |index| in the +discussion above, is a code number that explains what kind of token list +is being scanned. + +\yskip\hang|name| points to the |eqtb| address of the control sequence +being expanded, if the current token list is a macro. + +\yskip\hang|param_start|, which takes the place of |limit|, tells where +the parameters of the current macro begin in the |param_stack|, if the +current token list is a macro. + +\yskip\noindent The |token_type| can take several values, depending on +where the current token list came from: + +\yskip\hang|parameter|, if a parameter is being scanned; + +\hang|u_template|, if the \<u_j> part of an alignment +template is being scanned; + +\hang|v_template|, if the \<v_j> part of an alignment +template is being scanned; + +\hang|backed_up|, if the token list being scanned has been inserted as +`to be read again'; + +\hang|inserted|, if the token list being scanned has been inserted as +the text expansion of a \.{\\count} or similar variable; + +\hang|macro|, if a user-defined control sequence is being scanned; + +\hang|output_text|, if an \.{\\output} routine is being scanned; + +\hang|every_par_text|, if the text of \.{\\everypar} is being scanned; + +\hang|every_math_text|, if the text of \.{\\everymath} is being scanned; + +\hang|every_display_text|, if the text of \.{\\everydisplay} is being scanned; + +\hang|every_hbox_text|, if the text of \.{\\everyhbox} is being scanned; + +\hang|every_vbox_text|, if the text of \.{\\everyvbox} is being scanned; + +\hang|every_job_text|, if the text of \.{\\everyjob} is being scanned; + +\hang|every_cr_text|, if the text of \.{\\everycr} is being scanned; + +\hang|mark_text|, if the text of a \.{\\mark} is being scanned; + +\hang|write_text|, if the text of a \.{\\write} is being scanned. + +\yskip\noindent +The codes for |output_text|, |every_par_text|, etc., are equal to a constant +plus the corresponding codes for token list parameters |output_routine_loc|, +|every_par_loc|, etc. The token list begins with a reference count if and +only if |token_type >= macro|. +@^reference counts@> + +Since \eTeX's additional token list parameters precede |toks_base|, the +corresponding token types must precede |write_text|. + +@d token_list 0 /*|state| code when scanning a token list*/ +@d token_type index /*type of current token list*/ +@d param_start limit /*base of macro parameters in |param_stack|*/ +@d parameter 0 /*|token_type| code for parameter*/ +@d u_template 1 /*|token_type| code for \<u_j> template*/ +@d v_template 2 /*|token_type| code for \<v_j> template*/ +@d backed_up 3 /*|token_type| code for text to be reread*/ +@d inserted 4 /*|token_type| code for inserted texts*/ +@d macro 5 /*|token_type| code for defined control sequences*/ +@d output_text 6 /*|token_type| code for output routines*/ +@d every_par_text 7 /*|token_type| code for \.{\\everypar}*/ +@d every_math_text 8 /*|token_type| code for \.{\\everymath}*/ +@d every_display_text 9 /*|token_type| code for \.{\\everydisplay}*/ +@d every_hbox_text 10 /*|token_type| code for \.{\\everyhbox}*/ +@d every_vbox_text 11 /*|token_type| code for \.{\\everyvbox}*/ +@d every_job_text 12 /*|token_type| code for \.{\\everyjob}*/ +@d every_cr_text 13 /*|token_type| code for \.{\\everycr}*/ +@d mark_text 14 /*|token_type| code for \.{\\topmark}, etc.*/ +@# +@d eTeX_text_offset (output_routine_loc-output_text) +@d every_eof_text (every_eof_loc-eTeX_text_offset) + /*|token_type| code for \.{\\everyeof}*/ +@# +@d write_text (toks_base-eTeX_text_offset) /*|token_type| code for \.{\\write}*/ + +@ The |param_stack| is an auxiliary array used to hold pointers to the token +lists for parameters at the current level and subsidiary levels of input. +This stack is maintained with convention (2), and it grows at a different +rate from the others. + +@<Glob...@>= +static pointer @!param_stack[param_size+1]; + /*token list pointers for parameters*/ +static int @!param_ptr; /*first unused entry in |param_stack|*/ +static int @!max_param_stack; + /*largest value of |param_ptr|, will be | <= param_size+9|*/ + +@ The input routines must also interact with the processing of +\.{\\halign} and \.{\\valign}, since the appearance of tab marks and +\.{\\cr} in certain places is supposed to trigger the beginning of special +\<v_j> template text in the scanner. This magic is accomplished by an +|align_state| variable that is increased by~1 when a `\.{\char'173}' is +scanned and decreased by~1 when a `\.{\char'175}' is scanned. The |align_state| +is nonzero during the \<u_j> template, after which it is set to zero; the +\<v_j> template begins when a tab mark or \.{\\cr} occurs at a time that +|align_state==0|. + +The same principle applies when entering the definition of a +control sequence between \.{\\csname} and \.{\\endcsname}. + +@<Glob...@>= +static int @!align_state; /*group level with respect to current alignment*/ +static int @!incsname_state; /*group level with respect to in csname state*/ + +@ Thus, the ``current input state'' can be very complicated indeed; there +can be many levels and each level can arise in a variety of ways. The +|show_context| procedure, which is used by \TeX's error-reporting routine to +print out the current input state on all levels down to the most recent +line of characters from an input file, illustrates most of these conventions. +The global variable |base_ptr| contains the lowest level that was +displayed by this procedure. + +@<Glob...@>= +static int @!base_ptr; /*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 static void show_context(void) /*prints where the scanner is*/ +{@+ +int old_setting; /*saved |selector| setting*/ +int @!nn; /*number of contexts shown so far, less one*/ +bool @!bottom_line; /*have we reached the final context to be shown?*/ +@<Local variables for formatting calculations@>@/ +base_ptr=input_ptr;input_stack[base_ptr]=cur_input; + /*store current state*/ +nn=-1;bottom_line=false; +loop@+{@+cur_input=input_stack[base_ptr]; /*enter into the context*/ + if ((state!=token_list)) + if ((name > 19)||(base_ptr==0)) bottom_line=true; + if ((base_ptr==input_ptr)||bottom_line||(nn < error_context_lines)) + @<Display the current context@>@; + else if (nn==error_context_lines) + {@+print_nl("...");incr(nn); /*omitted if |error_context_lines < 0|*/ + } + if (bottom_line) goto done; + decr(base_ptr); + } +done: cur_input=input_stack[input_ptr]; /*restore original state*/ +} + +@ @<Display the current context@>= +{@+if ((base_ptr==input_ptr)||(state!=token_list)|| + (token_type!=backed_up)||(loc!=null)) + /*we omit backed-up token lists that have already been read*/ + {@+tally=0; /*get ready to count characters*/ + old_setting=selector; + if (state!=token_list) + {@+@<Print location of current line@>; + @<Pseudoprint the line@>; + } + else{@+@<Print type of token list@>; + @<Pseudoprint the token list@>; + } + selector=old_setting; /*stop pseudoprinting*/ + @<Print two lines using the tricky pseudoprinted information@>; + incr(nn); + } +} + +@ This routine should be changed, if necessary, to give the best possible +indication of where the current line resides in the input file. +For example, on some systems it is best to print both a page and line number. +@^system dependencies@> + +@<Print location of current line@>= +if (name <= 17) + if (terminal_input) + if (base_ptr==0) print_nl("<*>");else print_nl("<insert> "); + else{@+print_nl("<read "); + if (name==17) print_char('*');@+else print_int(name-1); +@.*\relax@> + print_char('>'); + } +else{@+print_nl("l."); + if (index==in_open) print_int(line); + else print_int(line_stack[index+1]); /*input from a pseudo file*/ + } +print_char(' ') + +@ @<Print type of token list@>= +switch (token_type) { +case parameter: print_nl("<argument> ");@+break; +case u_template: case v_template: print_nl("<template> ");@+break; +case backed_up: if (loc==null) print_nl("<recently read> "); + else print_nl("<to be read again> ");@+break; +case inserted: print_nl("<inserted text> ");@+break; +case macro: {@+print_ln();print_cs(name); + } @+break; +case output_text: print_nl("<output> ");@+break; +case every_par_text: print_nl("<everypar> ");@+break; +case every_math_text: print_nl("<everymath> ");@+break; +case every_display_text: print_nl("<everydisplay> ");@+break; +case every_hbox_text: print_nl("<everyhbox> ");@+break; +case every_vbox_text: print_nl("<everyvbox> ");@+break; +case every_job_text: print_nl("<everyjob> ");@+break; +case every_cr_text: print_nl("<everycr> ");@+break; +case mark_text: print_nl("<mark> ");@+break; +case every_eof_text: print_nl("<everyeof> ");@+break; +case write_text: print_nl("<write> ");@+break; +default:print_nl("?"); /*this should never happen*/ +} + +@ Here it is necessary to explain a little trick. We don't want to store a long +string that corresponds to a token list, because that string might take up +lots of memory; and we are printing during a time when an error message is +being given, so we dare not do anything that might overflow one of \TeX's +tables. So `pseudoprinting' is the answer: We enter a mode of printing +that stores characters into a buffer of length |error_line|, where character +$k+1$ is placed into \hbox{|trick_buf[k%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 dotdot 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)dotdot k-1]|,}$$ +where subscripts of |trick_buf| are circular modulo |error_line|. The +second line consists of |n|~spaces followed by |trick_buf[k dotdot(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...@>= +int @!i; /*index into |buffer|*/ +int @!j; /*end of current line in |buffer|*/ +int @!l; /*length of descriptive information on line 1*/ +int @!m; /*context information gathered for line 2*/ +int @!n; /*length of line 1*/ +int @!p; /*starting or ending place in |trick_buf|*/ +int @!q; /*temporary index*/ + +@ The following code sets up the print routines so that they will gather +the desired information. + +@d begin_pseudoprint + {@+l=tally;tally=0;selector=pseudo; + trick_count=1000000; + } +@d set_trick_count + {@+first_count=tally; + trick_count=tally+1+error_line-half_error_line; + if (trick_count < error_line) trick_count=error_line; + } + +@ And the following code uses the information after it has been gathered. + +@<Print two lines using the tricky pseudoprinted information@>= +if (trick_count==1000000) set_trick_count; + /*|set_trick_count| must be performed*/ +if (tally < trick_count) m=tally-first_count; +else m=trick_count-first_count; /*context on line 2*/ +if (l+first_count <= half_error_line) + {@+p=0;n=l+first_count; + } +else{@+print("...");p=l+first_count-half_error_line+3; + n=half_error_line; + } +for (q=p; q<=first_count-1; q++) print_char(trick_buf[q%error_line]); +print_ln(); +for (q=1; q<=n; q++) print_char(' '); /*print |n| spaces to begin line~2*/ +if (m+n <= error_line) p=first_count+m;else p=first_count+(error_line-n-3); +for (q=first_count; q<=p-1; q++) print_char(trick_buf[q%error_line]); +if (m+n > error_line) print("...") + +@ But the trick is distracting us from our current goal, which is to +understand the input state. So let's concentrate on the data structures that +are being pseudoprinted as we finish up the |show_context| procedure. + +@<Pseudoprint the line@>= +begin_pseudoprint; +if (buffer[limit]==end_line_char) j=limit; +else j=limit+1; /*determine the effective end of the line*/ +if (j > 0) for (i=start; i<=j-1; i++) + {@+if (i==loc) set_trick_count; + printn(buffer[i]); + } + +@ @<Pseudoprint the token list@>= +begin_pseudoprint; +if (token_type < macro) show_token_list(start, loc, 100000); +else show_token_list(link(start), loc, 100000) /*avoid reference count*/ + +@ Here is the missing piece of |show_token_list| that is activated when the +token beginning line~2 is about to be shown: + +@<Do magic computation@>=set_trick_count + +@* 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*/ + {@+if (input_ptr > max_in_stack) + {@+max_in_stack=input_ptr; + if (input_ptr==stack_size) overflow("input stack size", stack_size); +@:TeX capacity exceeded input stack size}{\quad input stack size@> + } + input_stack[input_ptr]=cur_input; /*stack the record*/ + incr(input_ptr); + } + +@ And of course what goes up must come down. + +@d pop_input @t@> /*leave an input level, re-enter the old*/ + {@+decr(input_ptr);cur_input=input_stack[input_ptr]; + } + +@ Here is a procedure that starts a new level of token-list input, given +a token list |p| and its type |t|. If |t==macro|, the calling routine should +set |name| and |loc|. + +@d back_list(A) begin_token_list(A, backed_up) /*backs up a simple token list*/ +@d ins_list(A) begin_token_list(A, inserted) /*inserts a simple token list*/ + +@p static void begin_token_list(pointer @!p, quarterword @!t) +{@+push_input;state=token_list;start=p;token_type=t; +if (t >= macro) /*the token list starts with a reference count*/ + {@+add_token_ref(p); + if (t==macro) param_start=param_ptr; + else{@+ + @<additional local variables for |begin_token_list|@> + loc=link(p); + if (tracing_macros > 1) + {@+begin_diagnostic();print_nl(""); + switch (t) { + case mark_text: print_esc("mark");@+break; + case write_text: print_esc("write");@+break; + default:print_cmd_chr(assign_toks, t-output_text+output_routine_loc); + } @/ + print("->");token_show(p);end_diagnostic(false); + } + @<update the macro stack@>@; + } + } +else loc=p; +} + +@ When a token list has been fully scanned, the following computations +should be done as we leave that level of input. The |token_type| tends +to be equal to either |backed_up| or |inserted| about 2/3 of the time. +@^inner loop@> + +@p static void end_token_list(void) /*leave a token-list input level*/ +{@+if (token_type >= backed_up) /*token list to be deleted*/ + {@+if (token_type <= inserted) flush_list(start); + else{@+delete_token_ref(start); /*update reference count*/ + if (token_type==macro) /*parameters must be flushed*/ + while (param_ptr > param_start) + {@+decr(param_ptr); + flush_list(param_stack[param_ptr]); + } + } + } +else if (token_type==u_template) + if (align_state > 500000) align_state=0; + else fatal_error("(interwoven alignment preambles are not allowed)"); +@.interwoven alignment preambles...@> +pop_input; +check_interrupt; +} + +@ Sometimes \TeX\ has read too far and wants to ``unscan'' what it has +seen. The |back_input| procedure takes care of this by putting the token +just scanned back into the input stream, ready to be read again. This +procedure can be used only if |cur_tok| represents the token to be +replaced. Some applications of \TeX\ use this procedure a lot, +so it has been slightly optimized for speed. +@^inner loop@> + +We charge the backup token to the current file and line. +@^profiling@> + +@p static void back_input(void) /*undoes one token of input*/ +{@+pointer p; /*a token list of length one*/ +while ((state==token_list)&&(loc==null)&&(token_type!=v_template)) + end_token_list(); /*conserve stack space*/ +p=get_avail();info(p)=cur_tok; fl_mem[p]=prof_file_line; +if (cur_tok < right_brace_limit) + if (cur_tok < left_brace_limit) decr(align_state); + else incr(align_state); +push_input;state=token_list;start=p;token_type=backed_up; +loc=p; /*that was |back_list(p)|, without procedure overhead*/ +} + +@ @<Insert token |p| into \TeX's input@>= +{@+t=cur_tok;cur_tok=p; +if (a) + {@+p=get_avail();info(p)=cur_tok;link(p)=loc;loc=p;start=p; + if (cur_tok < right_brace_limit) + if (cur_tok < left_brace_limit) decr(align_state); + else incr(align_state); + } +else{@+back_input();a=eTeX_ex; + } +cur_tok=t; +} + +@ The |back_error| routine is used when we want to replace an offending token +just before issuing an error message. This routine, like |back_input|, +requires that |cur_tok| has been set. We disable interrupts during the +call of |back_input| so that the help message won't be lost. + +@p static void back_error(void) /*back up one token and call |error|*/ +{@+OK_to_interrupt=false;back_input();OK_to_interrupt=true;error(); +} +@# +static void ins_error(void) /*back up one inserted token and call |error|*/ +{@+OK_to_interrupt=false;back_input();token_type=inserted; +OK_to_interrupt=true;error(); +} + +@ 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 static void begin_file_reading(void) +{@+if (in_open==max_in_open) overflow("text input levels", max_in_open); +@:TeX capacity exceeded text input levels}{\quad text input levels@> +if (first==buf_size) overflow("buffer size", buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> +incr(in_open);push_input;index=in_open;@/ +source_filename_stack[index]=NULL; /* \TeX\ Live */ +full_source_filename_stack[index]=NULL; /* \TeX\ Live */ +eof_seen[index]=false; +grp_stack[index]=cur_boundary;if_stack[index]=cond_ptr; +line_stack[index]=line;start=first;state=mid_line; +name=0; /*|terminal_input| is now |true|*/ +cur_file_num=terminal_file; +} + +@ Conversely, the variables must be downdated when such a level of input +is finished: + +@p static void end_file_reading(void) +{@+first=start;line=line_stack[index]; +if ((name==18)||(name==19)) pseudo_close();else +if (name > 17) a_close(&cur_file); /*forget it*/ +if (full_source_filename_stack[in_open]!=NULL) +{ free(full_source_filename_stack[in_open]); + full_source_filename_stack[in_open]=NULL; +} +pop_input;decr(in_open); +} + +@ 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 static void clear_for_error_prompt(void) +{@+while ((state!=token_list)&&terminal_input&&@| + (input_ptr > 0)&&(loc > limit)) end_file_reading(); +print_ln();clear_terminal; +} + +@ To get \TeX's whole input mechanism going, we perform the following +actions. + +@<Initialize the input routines@>= +{@+input_ptr=0;max_in_stack=0; +in_open=0;open_parens=0;max_buf_stack=0; +grp_stack[0]=0;if_stack[0]=null; +param_ptr=0;max_param_stack=0; +first=buf_size;@/do@+{buffer[first]=0;decr(first);}@+ while (!(first==0)); +scanner_status=normal;warning_index=null;first=1; +state=new_line;start=1;index=0;line=0;name=0;cur_depth=0; +force_eof=false; +align_state=1000000;@/ +if (!init_terminal()) exit(0); +limit=last;first=last+1; /*|init_terminal| has set |loc| and |last|*/ +} + +@* Getting the next token. +The heart of \TeX's input mechanism is the |get_next| procedure, which +we shall develop in the next few sections of the program. Perhaps we +shouldn't actually call it the ``heart,'' however, because it really acts +as \TeX's eyes and mouth, reading the source files and gobbling them up. +And it also helps \TeX\ to regurgitate stored token lists that are to be +processed again. +@^eyes and mouth@> + +The main duty of |get_next| is to input one token and to set |cur_cmd| +and |cur_chr| to that token's command code and modifier. Furthermore, if +the input token is a control sequence, the |eqtb| location of that control +sequence is stored in |cur_cs|; otherwise |cur_cs| is set to zero. + +Underlying this simple description is a certain amount of complexity +because of all the cases that need to be handled. +However, the inner loop of |get_next| is reasonably short and fast. + +When |get_next| is asked to get the next token of a \.{\\read} line, +it sets |cur_cmd==cur_chr==cur_cs==0| in the case that no more tokens +appear on that line. (There might not be any tokens at all, if the +|end_line_char| has |ignore| as its catcode.) + +@ The value of |par_loc| is the |eqtb| address of `\.{\\par}'. This quantity +is needed because a blank line of input is supposed to be exactly equivalent +to the appearance of \.{\\par}; we must set |cur_cs:=par_loc| +when detecting a blank line. + +The same is true for the input, for the warning message, since input +is expected by default before every scanning and hence setting of +|cur_cs|. + +@<Glob...@>= +static pointer @!par_loc; /*location of `\.{\\par}' in |eqtb|*/ +static halfword @!par_token; /*token representing `\.{\\par}'*/ +static pointer @!input_loc; /*location of `\.{\\input}' in |eqtb|*/ +static halfword @!input_token; /*token representing `\.{\\input}'*/ + +@ @<Put each...@>= +primitive("par", par_end, 256); /*cf.\ |scan_file_name|*/ +@!@:par\_}{\.{\\par} primitive@> +par_loc=cur_val;par_token=cs_token_flag+par_loc; + +@ @<Cases of |print_cmd_chr|...@>= +case par_end: print_esc("par");@+break; + +@ Before getting into |get_next|, let's consider the subroutine that +is called when an `\.{\\outer}' control sequence has been scanned or +when the end of a file has been reached. These two cases are distinguished +by |cur_cs|, which is zero at the end of a file. + +@p static void check_outer_validity(void) +{@+pointer p; /*points to inserted token list*/ +pointer @!q; /*auxiliary pointer*/ +if (scanner_status!=normal) + {@+deletions_allowed=false; + @<Back up an outer control sequence so that it can be reread@>; + if (scanner_status > skipping) + @<Tell the user what has run away and try to recover@>@; + else{@+print_err("Incomplete ");print_cmd_chr(if_test, cur_if); +@.Incomplete \\if...@> + print("; all text was ignored after line ");print_int(skip_line); + help3("A forbidden control sequence occurred in skipped text.",@/ + "This kind of error happens when you say `\\if...' and forget",@/ + "the matching `\\fi'. I've inserted a `\\fi'; this might work."); + if (cur_cs!=0) cur_cs=0; + else help_line[2]=@| + "The file ended while I was skipping conditional text."; + cur_tok=cs_token_flag+frozen_fi;ins_error(); + } + deletions_allowed=true; + } +} + +@ An outer control sequence that occurs in a \.{\\read} will not be reread, +since the error recovery for \.{\\read} is not very powerful. + +@<Back up an outer control sequence so that it can be reread@>= +if (cur_cs!=0) + {@+if ((state==token_list)||(name < 1)||(name > 17)) + {@+p=get_avail();info(p)=cs_token_flag+cur_cs; + back_list(p); /*prepare to read the control sequence again*/ + } + cur_cmd=spacer;cur_chr=' '; /*replace it by a space*/ + } + +@ @<Tell the user what has run away...@>= +{@+runaway(); /*print a definition, argument, or preamble*/ +if (cur_cs==0) print_err("File ended"); +@.File ended while scanning...@> +else{@+cur_cs=0;print_err("Forbidden control sequence found"); +@.Forbidden control sequence...@> + } +print(" while scanning "); +@<Print either `\.{definition}' or `\.{use}' or `\.{preamble}' or `\.{text}', +and insert tokens that should lead to recovery@>; +print(" of ");sprint_cs(warning_index); +help4("I suspect you have forgotten a `}', causing me",@/ +"to read past where you wanted me to stop.",@/ +"I'll try to recover; but if the error is serious,",@/ +"you'd better type `E' or `X' now and fix your file.");@/ +error(); +} + +@ The recovery procedure can't be fully understood without knowing more +about the \TeX\ routines that should be aborted, but we can sketch the +ideas here: For a runaway definition or a runaway balanced text +we will insert a right brace; for a +runaway preamble, we will insert a special \.{\\cr} token and a right +brace; and for a runaway argument, we will set |long_state| to +|outer_call| and insert \.{\\par}. + +@<Print either `\.{definition}' or...@>= +p=get_avail(); +switch (scanner_status) { +case defining: {@+print("definition");info(p)=right_brace_token+'}'; + } @+break; +case matching: {@+print("use");info(p)=par_token;long_state=outer_call; + } @+break; +case aligning: {@+print("preamble");info(p)=right_brace_token+'}';q=p; + p=get_avail();link(p)=q;info(p)=cs_token_flag+frozen_cr; + align_state=-1000000; + } @+break; +case absorbing: {@+print("text");info(p)=right_brace_token+'}'; + } +} /*there are no other cases*/ +ins_list(p) + +@ We need to mention a procedure here that may be called by |get_next|. + +@p static void firm_up_the_line(void); + +@ Now we're ready to take the plunge into |get_next| itself. Parts of +this routine are executed more often than any other instructions of \TeX. +@^mastication@>@^inner loop@> + +@p static void get_next(void) /*sets |cur_cmd|, |cur_chr|, |cur_cs| to next token*/ +{@+ /*go here to get the next input token*/ + /*go here to eat the next character from a file*/ + /*go here to digest it again*/ + /*go here to start looking for a control sequence*/ + /*go here when a control sequence has been found*/ + /*go here when the next input token has been got*/ +int k; /*an index into |buffer|*/ +halfword @!t; /*a token*/ +int @!cat; /*|cat_code(cur_chr)|, usually*/ +ASCII_code @!c, @!cc; /*constituents of a possible expanded code*/ +int @!d; /*number of excess characters in an expanded code*/ +restart: cur_cs=0; +if (state!=token_list) +@<Input from external file, |goto restart| if no input found@>@; +else@<Input from token list, |goto restart| if end of list or if a parameter +needs to be expanded@>; +@<If an alignment entry has just ended, take appropriate action@>; +} + +@ An alignment entry ends when a tab or \.{\\cr} occurs, provided that the +current level of braces is the same as the level that was present at the +beginning of that alignment entry; i.e., provided that |align_state| has +returned to the value it had after the \<u_j> template for that entry. +@^inner loop@> + +@<If an alignment entry has just ended, take appropriate action@>= +if (cur_cmd <= car_ret) if (cur_cmd >= tab_mark) if (align_state==0) + @<Insert the \(v)\<v_j> template and |goto restart|@>@; + +@ @<Input from external file, |goto restart| if no input found@>= +@^inner loop@> +{@+get_cur_chr: if (loc <= limit) /*current line not yet finished*/ + {@+cur_chr=buffer[loc];incr(loc); + @<Set |cur_file_line| based on the information in |cur_input|@>@; + reswitch: cur_cmd=cat_code(cur_chr); + @<Change state if necessary, and |goto get_cur_chr| if the current character +should be ignored, or |goto reswitch| if the current character changes to +another@>; + } +else{@+state=new_line;@/ + @<Move to next line of file, or |goto restart| if there is no next line, +or |return| if a \.{\\read} line has finished@>; + check_interrupt; + goto get_cur_chr; + } +} + +@ The following 48-way switch accomplishes the scanning quickly, assuming +that a decent \PASCAL\ compiler has translated the code. Note that the numeric +values for |mid_line|, |skip_blanks|, and |new_line| are spaced +apart from each other by |max_char_code+1|, so we can add a character's +command code to the state to get a single number that characterizes both. + +@d any_state_plus(A) case mid_line+A: + case skip_blanks+A: case new_line+A + +@<Change state if necessary...@>= +switch (state+cur_cmd) { +@<Cases where character is ignored@>: goto get_cur_chr; +any_state_plus(escape): @<Scan a control sequence and set |state:=skip_blanks| +or |mid_line|@>@;@+break; +any_state_plus(active_char): @<Process an active-character control sequence +and set |state:=mid_line|@>@;@+break; +any_state_plus(sup_mark): @<If this |sup_mark| starts an expanded character +like~\.{\^\^A} or~\.{\^\^df}, then |goto reswitch|, otherwise set |state:=mid_line|@>@;@+break; +any_state_plus(invalid_char): @<Decry the invalid character and |goto restart|@>@; +@t\4@>@<Handle situations involving spaces, braces, changes of state@>@; +default:do_nothing; +} + +@ @<Cases where character is ignored@>= +any_state_plus(ignore): case skip_blanks+spacer: + case new_line+spacer + +@ We go to |restart| instead of to |get_cur_chr|, because |state| might equal +|token_list| after the error has been dealt with +(cf.\ |clear_for_error_prompt|). + +@<Decry the invalid...@>= +{@+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; +} + +@ @d add_delims_to(A) A+math_shift: A+tab_mark: A+mac_param: + A+sub_mark: A+letter: A+other_char + +@<Handle situations involving spaces, braces, changes of state@>= +case mid_line+spacer: @<Enter |skip_blanks| state, emit a space@>@;@+break; +case mid_line+car_ret: @<Finish line, emit a space@>@;@+break; +case skip_blanks+car_ret: any_state_plus(comment): + @<Finish line, |goto switch|@>@; +case new_line+car_ret: @<Finish line, emit a \.{\\par}@>@;@+break; +case mid_line+left_brace: incr(align_state);@+break; +case skip_blanks+left_brace: case new_line+left_brace: {@+ + state=mid_line;incr(align_state); + } @+break; +case mid_line+right_brace: decr(align_state);@+break; +case skip_blanks+right_brace: case new_line+right_brace: {@+ + state=mid_line;decr(align_state); + } @+break; +add_delims_to(case skip_blanks): add_delims_to( + case new_line): state=mid_line;@+break; + +@ When a character of type |spacer| gets through, its character code is +changed to $\.{"\ "}=040$. This means that the ASCII codes for tab and space, +and for the space inserted at the end of a line, will +be treated alike when macro parameters are being matched. We do this +since such characters are indistinguishable on most computer terminal displays. + +@<Finish line, emit a space@>= +{@+loc=limit+1;cur_cmd=spacer;cur_chr=' '; +} + +@ The following code is performed only when |cur_cmd==spacer|. + +@<Enter |skip_blanks| state, emit a space@>= +{@+state=skip_blanks;cur_chr=' '; +} + +@ @<Finish line, |goto switch|@>= +{@+loc=limit+1;goto get_cur_chr; +} + +@ @<Finish line, emit a \.{\\par}@>= +{@+loc=limit+1;cur_cs=par_loc;cur_cmd=eq_type(cur_cs); +cur_chr=equiv(cur_cs); +if (cur_cmd >= outer_call) check_outer_validity(); +} + +@ Notice that a code like \.{\^\^8} becomes \.x if not followed by a hex digit. + +@d is_hex(A) (((A >= '0')&&(A <= '9'))||((A >= 'a')&&(A <= 'f'))) +@d hex_to_cur_chr + if (c <= '9') cur_chr=c-'0';@+else cur_chr=c-'a'+10; + if (cc <= '9') cur_chr=16*cur_chr+cc-'0'; + else cur_chr=16*cur_chr+cc-'a'+10 + +@<If this |sup_mark| starts an expanded character...@>= +{@+if (cur_chr==buffer[loc]) if (loc < limit) + {@+c=buffer[loc+1];@+if (c < 0200) /*yes we have an expanded char*/ + {@+loc=loc+2; + if (is_hex(c)) if (loc <= limit) + {@+cc=buffer[loc];@+if (is_hex(cc)) + {@+incr(loc);hex_to_cur_chr;goto reswitch; + } + } + if (c < 0100) cur_chr=c+0100;@+else cur_chr=c-0100; + goto reswitch; + } + } +state=mid_line; +} + +@ @<Process an active-character...@>= +{@+cur_cs=cur_chr+active_base; +cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs);state=mid_line; +if (cur_cmd >= outer_call) check_outer_validity(); +} + +@ Control sequence names are scanned only when they appear in some line of +a file; once they have been scanned the first time, their |eqtb| location +serves as a unique identification, so \TeX\ doesn't need to refer to the +original name any more except when it prints the equivalent in symbolic form. + +The program that scans a control sequence has been written carefully +in order to avoid the blowups that might otherwise occur if a malicious +user tried something like `\.{\\catcode\'15=0}'. The algorithm might +look at |buffer[limit+1]|, but it never looks at |buffer[limit+2]|. + +If expanded characters like `\.{\^\^A}' or `\.{\^\^df}' +appear in or just following +a control sequence name, they are converted to single characters in the +buffer and the process is repeated, slowly but surely. + +@<Scan a control...@>= +{@+if (loc > limit) cur_cs=null_cs; /*|state| is irrelevant in this case*/ +else{@+start_cs: k=loc;cur_chr=buffer[k];cat=cat_code(cur_chr); + incr(k); + if (cat==letter) state=skip_blanks; + else if (cat==spacer) state=skip_blanks; + else state=mid_line; + if ((cat==letter)&&(k <= limit)) + @<Scan ahead in the buffer until finding a nonletter; if an expanded code +is encountered, reduce it and |goto start_cs|; otherwise if a multiletter +control sequence is found, adjust |cur_cs| and |loc|, and |goto found|@>@; + else@<If an expanded code is present, reduce it and |goto start_cs|@>; + cur_cs=single_base+buffer[loc];incr(loc); + } +found: cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs); +if (cur_cmd >= outer_call) check_outer_validity(); +} + +@ Whenever we reach the following piece of code, we will have +|cur_chr==buffer[k-1]| and |k <= limit+1| and |cat==cat_code(cur_chr)|. If an +expanded code like \.{\^\^A} or \.{\^\^df} appears in |buffer[(k-1)dotdot(k+1)]| +or |buffer[(k-1)dotdot(k+2)]|, we +will store the corresponding code in |buffer[k-1]| and shift the rest of +the buffer left two or three places. + +@<If an expanded...@>= +{@+if (buffer[k]==cur_chr) @+if (cat==sup_mark) @+if (k < limit) + {@+c=buffer[k+1];@+if (c < 0200) /*yes, one is indeed present*/ + {@+d=2; + if (is_hex(c)) @+if (k+2 <= limit) + {@+cc=buffer[k+2];@+if (is_hex(cc)) incr(d); + } + if (d > 2) + {@+hex_to_cur_chr;buffer[k-1]=cur_chr; + } + else if (c < 0100) buffer[k-1]=c+0100; + else buffer[k-1]=c-0100; + limit=limit-d;first=first-d; + while (k <= limit) + {@+buffer[k]=buffer[k+d];incr(k); + } + goto start_cs; + } + } +} + +@ @<Scan ahead in the buffer...@>= +{@+@/do@+{cur_chr=buffer[k];cat=cat_code(cur_chr);incr(k); +}@+ while (!((cat!=letter)||(k > limit))); +@<If an expanded...@>; +if (cat!=letter) decr(k); + /*now |k| points to first nonletter*/ +if (k > loc+1) /*multiletter control sequence has been scanned*/ + {@+cur_cs=id_lookup(loc, k-loc);loc=k;goto found; + } +} + +@ Let's consider now what happens when |get_next| is looking at a token list. +We restore the file and line information. +@^profiling@> + +@<Input from token list, |goto restart| if end of list or if a parameter needs +to be expanded@>= +if (loc!=null) /*list not exhausted*/ +@^inner loop@> + {@+t=info(loc); cur_file_line=fl_mem[loc]; + loc=link(loc); /*move to next*/ + if (t >= cs_token_flag) /*a control sequence token*/ + {@+cur_cs=t-cs_token_flag; + cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs); + if (cur_cmd >= outer_call) + if (cur_cmd==dont_expand) + @<Get the next token, suppressing expansion@>@; + else check_outer_validity(); + } + else{@+cur_cmd=t/0400;cur_chr=t%0400; + switch (cur_cmd) { + case left_brace: incr(align_state);@+break; + case right_brace: decr(align_state);@+break; + case out_param: @<Insert macro parameter and |goto restart|@>@; + default:do_nothing; + } + } + } +else{@+ /*we are done with this token list*/ + end_token_list();goto restart; /*resume previous level*/ + } + +@ The present point in the program is reached only when the |expand| +routine has inserted a special marker into the input. In this special +case, |info(loc)| is known to be a control sequence token, and |link(loc)==null|. + +@d no_expand_flag 257 /*this characterizes a special variant of |relax|*/ + +@<Get the next token, suppressing expansion@>= +{@+cur_cs=info(loc)-cs_token_flag;loc=null;@/ +cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs); +if (cur_cmd > max_command) + {@+cur_cmd=relax;cur_chr=no_expand_flag; + } +} + +@ @<Insert macro parameter...@>= +{@+begin_token_list(param_stack[param_start+cur_chr-1], parameter); +goto restart; +} + +@ All of the easy branches of |get_next| have now been taken care of. +There is one more branch. + +@d end_line_char_inactive (end_line_char < 0)||(end_line_char > 255) + +@<Move to next line of file, or |goto restart|...@>= +if (name > 17) @<Read next line of file into |buffer|, or |goto restart| if +the file has ended@>@; +else{@+if (!terminal_input) /*\.{\\read} line has ended*/ + {@+cur_cmd=0;cur_chr=0; + @<Set |cur_file_line| when a \.{\\read} line ends@>@; + return; + } + if (input_ptr > 0) /*text was inserted during error recovery*/ + {@+end_file_reading();goto restart; /*resume previous level*/ + } + if (selector < log_only) open_log_file(); + if (interaction > nonstop_mode) + {@+if (end_line_char_inactive) incr(limit); + if (limit==start) /*previous line was empty*/ + print_nl("(Please type a command or say `\\end')"); +@.Please type...@> + print_ln();first=start; + prompt_input("*"); /*input on-line into |buffer|*/ +@.*\relax@> + limit=last; + if (end_line_char_inactive) decr(limit); + else buffer[limit]=end_line_char; + first=limit+1; + loc=start; + } + 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*/ + } + +@ The global variable |force_eof| is normally |false|; it is set |true| +by an \.{\\endinput} command. + +@<Glob...@>= +static bool @!force_eof; /*should the next \.{\\input} be aborted early?*/ + +@ @<Read next line of file into |buffer|, or |goto restart| if the file has +ended@>= +{@+incr(line);first=start; +@<check |line| for overflow@>@; +if (!force_eof) + if (name <= 19) + {@+if (pseudo_input()) /*not end of file*/ + firm_up_the_line(); /*this sets |limit|*/ + else if ((every_eof!=null)&&!eof_seen[index]) + {@+limit=first-1;eof_seen[index]=true; /*fake one empty line*/ + begin_token_list(every_eof, every_eof_text);goto restart; + } + else force_eof=true; + } + else + {@+if (input_ln(&cur_file, true)) /*not end of file*/ + firm_up_the_line(); /*this sets |limit|*/ + else if ((every_eof!=null)&&!eof_seen[index]) + {@+limit=first-1;eof_seen[index]=true; /*fake one empty line*/ + begin_token_list(every_eof, every_eof_text);goto restart; + } + else force_eof=true; + } +if (force_eof) + {@+if (tracing_nesting > 0) + if ((grp_stack[in_open]!=cur_boundary)||@| + (if_stack[in_open]!=cond_ptr)) file_warning(); + /*give warning for some unfinished groups and/or conditionals*/ + if (name >= 19) + {@+print_char(')');decr(open_parens); + update_terminal; /*show user that file has been read*/ + } + force_eof=false; + end_file_reading(); /*resume previous level*/ + check_outer_validity();goto restart; + } +if (end_line_char_inactive) decr(limit); +else buffer[limit]=end_line_char; +first=limit+1;loc=start; /*ready to read*/ +} + +@ If the user has set the |pausing| parameter to some positive value, +and if nonstop mode has not been selected, each line of input is displayed +on the terminal and the transcript file, followed by `\.{=>}'. +\TeX\ waits for a response. If the response is simply |carriage_return|, the +line is accepted as it stands, otherwise the line typed is +used instead of the line in the file. + +@p static void firm_up_the_line(void) +{@+int k; /*an index into |buffer|*/ +limit=last; +if (pausing > 0) if (interaction > nonstop_mode) + {@+wake_up_terminal;print_ln(); + if (start < limit) for (k=start; k<=limit-1; k++) printn(buffer[k]); + first=limit;prompt_input("=>"); /*wait for user response*/ +@.=>@> + if (last > first) + {@+for (k=first; k<=last-1; k++) /*move line down in buffer*/ + buffer[k+start-first]=buffer[k]; + limit=start+last-first; + } + } +} + +@ Since |get_next| is used so frequently in \TeX, it is convenient +to define three related procedures that do a little more: + +\yskip\hang|get_token| not only sets |cur_cmd| and |cur_chr|, it +also sets |cur_tok|, a packed halfword version of the current token. + +\yskip\hang|get_x_token|, meaning ``get an expanded token,'' is like +|get_token|, but if the current token turns out to be a user-defined +control sequence (i.e., a macro call), or a conditional, +or something like \.{\\topmark} or \.{\\expandafter} or \.{\\csname}, +it is eliminated from the input by beginning the expansion of the macro +or the evaluation of the conditional. + +\yskip\hang|x_token| is like |get_x_token| except that it assumes that +|get_next| has already been called. + +\yskip\noindent +In fact, these three procedures account for almost every use of |get_next|. + +@ No new control sequences will be defined except during a call of +|get_token|, or when \.{\\csname} compresses a token list, because +|no_new_control_sequence| is always |true| at other times. + +@p static void get_token(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|*/ +{@+no_new_control_sequence=false;get_next();no_new_control_sequence=true; +@^inner loop@> +if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr; +else cur_tok=cs_token_flag+cur_cs; +} + +@* Expanding the next token. +Only a dozen or so command codes | > max_command| can possibly be returned by +|get_next|; in increasing order, they are |undefined_cs|, |expand_after|, +|no_expand|, |input|, |if_test|, |fi_or_else|, |cs_name|, |convert|, |the|, +|top_bot_mark|, |call|, |long_call|, |outer_call|, |long_outer_call|, and +|end_template|.{\emergencystretch=40pt\par} + +The |expand| subroutine is used when |cur_cmd > max_command|. It removes a +``call'' or a conditional or one of the other special operations just +listed. It follows that |expand| might invoke itself recursively. In all +cases, |expand| destroys the current token, but it sets things up so that +the next |get_next| will deliver the appropriate next token. The value of +|cur_tok| need not be known when |expand| is called. + +Since several of the basic scanning routines communicate via global variables, +their values are saved as local variables of |expand| so that +recursive calls don't invalidate them. +@^recursion@> + +@p @t\4@>@<Declare the procedure called |macro_call|@>@;@/ +@t\4@>@<Declare the procedure called |insert_relax|@>@;@/ +@t\4@>@<Declare \eTeX\ procedures for expanding@>@;@/ +static void pass_text(void); +static void start_input(void); +static void conditional(void); +static void get_x_token(void); +static void conv_toks(void); +static void ins_the_toks(void); +static void expand(void) +{@+ +halfword t; /*token that is being ``expanded after''*/ +pointer @!p, @!q, @!r; /*for list manipulation*/ +int @!j; /*index into |buffer|*/ +int @!cv_backup; /*to save the global quantity |cur_val|*/ +small_number @!cvl_backup, @!radix_backup, @!co_backup; + /*to save |cur_val_level|, etc.*/ +pointer @!backup_backup; /*to save |link(backup_head)|*/ +small_number @!save_scanner_status; /*temporary storage of |scanner_status|*/ +cv_backup=cur_val;cvl_backup=cur_val_level;radix_backup=radix; +co_backup=cur_order;backup_backup=link(backup_head); +reswitch: +if (cur_cmd < call) @<Expand a nonmacro@>@; +else if (cur_cmd < end_template) macro_call(); +else@<Insert a token containing |frozen_endv|@>; +cur_val=cv_backup;cur_val_level=cvl_backup;radix=radix_backup; +cur_order=co_backup;link(backup_head)=backup_backup; +} + +@ @<Expand a nonmacro@>= +{@+if (tracing_commands > 1) show_cur_cmd_chr(); +switch (cur_cmd) { +case top_bot_mark: @<Insert the \(a)appropriate mark text into the scanner@>@;@+break; +case expand_after: switch (cur_chr) { +case 0: @<Expand the token after the next token@>@;@+break; +case 1: @<Negate a boolean conditional and |goto reswitch|@>@;@+break; +@/@<Cases for |expandafter|@>@/ +} @+break; /*there are no other cases*/ +case no_expand: @<Suppress expansion of the next token@>@;@+break; +case cs_name: @<Manufacture a control sequence name@>@;@+break; +case convert: conv_toks();@+break; /*this procedure is discussed in Part 27 below*/ +case the: ins_the_toks();@+break; /*this procedure is discussed in Part 27 below*/ +case if_test: conditional();@+break; /*this procedure is discussed in Part 28 below*/ +case fi_or_else: @<Terminate the current conditional and skip to \.{\\fi}@>@;@+break; +case input: @<Initiate or terminate input from a file@>;@+break; +default:@<Complain about an undefined macro@>@; +} +} + +@ It takes only a little shuffling to do what \TeX\ calls \.{\\expandafter}. + +@<Expand the token after...@>= +{@+get_token();t=cur_tok;get_token(); +if (cur_cmd > max_command) expand();@+else back_input(); +cur_tok=t;back_input(); +} + +@ The implementation of \.{\\noexpand} is a bit trickier, because it is +necessary to insert a special `|dont_expand|' marker into \TeX's reading +mechanism. This special marker is processed by |get_next|, but it does +not slow down the inner loop. + +Since \.{\\outer} macros might arise here, we must also +clear the |scanner_status| temporarily. + +@<Suppress expansion...@>= +{@+save_scanner_status=scanner_status;scanner_status=normal; +get_token();scanner_status=save_scanner_status;t=cur_tok; +back_input(); /*now |start| and |loc| point to the backed-up token |t|*/ +if (t >= cs_token_flag) + {@+p=get_avail();info(p)=cs_token_flag+frozen_dont_expand; + link(p)=loc;start=p;loc=p; + } +} + +@ @<Complain about an undefined macro@>= +{@+print_err("Undefined control sequence"); +@.Undefined control sequence@> +help5("The control sequence at the end of the top line",@/ +"of your error message was never \\def'ed. If you have",@/ +"misspelled it (e.g., `\\hobx'), type `I' and the correct",@/ +"spelling (e.g., `I\\hbox'). Otherwise just continue,",@/ +"and I'll forget about whatever was undefined."); +error(); +} + +@ The |expand| procedure and some other routines that construct token +lists find it convenient to use the following macros, which are valid only if +the variables |p| and |q| are reserved for token-list building. +Here we add code to store file and line information for each token. +@^profiling@> + +@d store_new_token(A) {@+q=get_avail();link(p)=q;info(q)=A; + fl_mem[q]=cur_file_line; + p=q; /*|link(p)| is |null|*/ + } +@d fast_store_new_token(A) {@+fast_get_avail(q);link(p)=q;info(q)=A; + fl_mem[q]=cur_file_line; + p=q; /*|link(p)| is |null|*/ + } + +@ @<Manufacture a control...@>= +{@+r=get_avail();p=r; /*head of the list of characters*/ +incr(incsname_state); +@/do@+{get_x_token(); +if (cur_cs==0) store_new_token(cur_tok); +}@+ while (!(cur_cs!=0)); +if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>; +decr(incsname_state); +@<Look up the characters of list |r| in the hash table, and set |cur_cs|@>; +flush_list(r); +if (eq_type(cur_cs)==undefined_cs) + {@+eq_define(cur_cs, relax, 256); /*N.B.: The |save_stack| might change*/ + } /*the control sequence will now match `\.{\\relax}'*/ +cur_tok=cur_cs+cs_token_flag;back_input(); +} + +@ @<Complain about missing \.{\\endcsname}@>= +{@+print_err("Missing ");print_esc("endcsname");print(" inserted"); +@.Missing \\endcsname...@> +help2("The control sequence marked <to be read again> should",@/ + "not appear between \\csname and \\endcsname."); +back_error(); +} + +@ @<Look up the characters of list |r| in the hash table...@>= +j=first;p=link(r); +while (p!=null) + {@+if (j >= max_buf_stack) + {@+max_buf_stack=j+1; + if (max_buf_stack==buf_size) + overflow("buffer size", buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> + } + buffer[j]=info(p)%0400;incr(j);p=link(p); + } +if (j==first) cur_cs=null_cs; /*the list is empty*/ +else if (j > first+1) + {@+no_new_control_sequence=false;cur_cs=id_lookup(first, j-first); + no_new_control_sequence=true; + } +else cur_cs=single_base+buffer[first] /*the list has length one*/ + +@ An |end_template| command is effectively changed to an |endv| command +by the following code. (The reason for this is discussed below; the +|frozen_end_template| at the end of the template has passed the +|check_outer_validity| test, so its mission of error detection has been +accomplished.) + +@<Insert a token containing |frozen_endv|@>= +{@+cur_tok=cs_token_flag+frozen_endv;back_input(); +} + +@ 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@> +input_loc=cur_val;input_token=cs_token_flag+input_loc; +primitive("endinput", input, 1);@/ +@!@:end\_input\_}{\.{\\endinput} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case input: if (chr_code==0) print_esc("input") + @/@<Cases of |input| for |print_cmd_chr|@>;@/ + else print_esc("endinput");@+break; + +@ @<Initiate or terminate input...@>= +if (cur_chr==1) force_eof=true +@/@<Cases for |input|@>;@/ +else if (name_in_progress) insert_relax(); +else start_input() + +@ Sometimes the expansion looks too far ahead, so we want to insert +a harmless \.{\\relax} into the user's input. + +@<Declare the procedure called |insert_relax|@>= +static void insert_relax(void) +{@+cur_tok=cs_token_flag+cur_cs;back_input(); +cur_tok=cs_token_flag+frozen_relax;back_input();token_type=inserted; +} + +@ Here is a recursive procedure that is \TeX's usual way to get the +next token of input. It has been slightly optimized to take account of +common cases. + +@p static void get_x_token(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|, + and expands macros*/ +{@+ +restart: get_next(); +@^inner loop@> +if (cur_cmd <= max_command) goto done; +if (cur_cmd >= call) + if (cur_cmd < end_template) macro_call(); + else{@+cur_cs=frozen_endv;cur_cmd=endv; + goto done; /*|cur_chr==null_list|*/ + } +else expand(); +goto restart; +done: if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr; +else cur_tok=cs_token_flag+cur_cs; +} + +@ The |get_x_token| procedure is essentially equivalent to two consecutive +procedure calls: |get_next;x_token|. + +@p static void x_token(void) /*|get_x_token| without the initial |get_next|*/ +{@+while (cur_cmd > max_command) + {@+expand(); + get_next(); + } +if (cur_cs==0) cur_tok=(cur_cmd*0400)+cur_chr; +else cur_tok=cs_token_flag+cur_cs; +} + +@ A control sequence that has been \.{\\def}'ed by the user is expanded by +\TeX's |macro_call| procedure. + +Before we get into the details of |macro_call|, however, let's consider the +treatment of primitives like \.{\\topmark}, since they are essentially +macros without parameters. The token lists for such marks are kept in a +global array of five pointers; we refer to the individual entries of this +array by symbolic names |top_mark|, etc. The value of |top_mark| is either +|null| or a pointer to the reference count of a token list. + +@d marks_code 5 /*add this for \.{\\topmarks} etc.*/ +@# +@d top_mark_code 0 /*the mark in effect at the previous page break*/ +@d first_mark_code 1 /*the first mark between |top_mark| and |bot_mark|*/ +@d bot_mark_code 2 /*the mark in effect at the current page break*/ +@d split_first_mark_code 3 /*the first mark found by \.{\\vsplit}*/ +@d split_bot_mark_code 4 /*the last mark found by \.{\\vsplit}*/ +@d top_mark cur_mark[top_mark_code] +@d first_mark cur_mark[first_mark_code] +@d bot_mark cur_mark[bot_mark_code] +@d split_first_mark cur_mark[split_first_mark_code] +@d split_bot_mark cur_mark[split_bot_mark_code] + +@<Glob...@>= +static pointer @!cur_mark0[split_bot_mark_code-top_mark_code+1], + *const @!cur_mark = @!cur_mark0-top_mark_code; + /*token lists for marks*/ + +@ @<Set init...@>= +top_mark=null;first_mark=null;bot_mark=null; +split_first_mark=null;split_bot_mark=null; + +@ @<Put each...@>= +primitive("topmark", top_bot_mark, top_mark_code); +@!@:top\_mark\_}{\.{\\topmark} primitive@> +primitive("firstmark", top_bot_mark, first_mark_code); +@!@:first\_mark\_}{\.{\\firstmark} primitive@> +primitive("botmark", top_bot_mark, bot_mark_code); +@!@:bot\_mark\_}{\.{\\botmark} primitive@> +primitive("splitfirstmark", top_bot_mark, split_first_mark_code); +@!@:split\_first\_mark\_}{\.{\\splitfirstmark} primitive@> +primitive("splitbotmark", top_bot_mark, split_bot_mark_code); +@!@:split\_bot\_mark\_}{\.{\\splitbotmark} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case top_bot_mark: {@+switch ((chr_code%marks_code)) { + case first_mark_code: print_esc("firstmark");@+break; + case bot_mark_code: print_esc("botmark");@+break; + case split_first_mark_code: print_esc("splitfirstmark");@+break; + case split_bot_mark_code: print_esc("splitbotmark");@+break; + default:print_esc("topmark"); + } + if (chr_code >= marks_code) print_char('s'); + } @+break; + +@ The following code is activated when |cur_cmd==top_bot_mark| and +when |cur_chr| is a code like |top_mark_code|. + +@<Insert the \(a)appropriate mark text into the scanner@>= +{@+t=cur_chr%marks_code; +if (cur_chr >= marks_code) scan_register_num();@+else cur_val=0; +if (cur_val==0) cur_ptr=cur_mark[t]; +else@<Compute the mark pointer for mark type |t| and class |cur_val|@>; +if (cur_ptr!=null) begin_token_list(cur_ptr, mark_text); +} + +@ Now let's consider |macro_call| itself, which is invoked when \TeX\ is +scanning a control sequence whose |cur_cmd| is either |call|, |long_call|, +|outer_call|, or |long_outer_call|. The control sequence definition +appears in the token list whose reference count is in location |cur_chr| +of |mem|. + +The global variable |long_state| will be set to |call| or to |long_call|, +depending on whether or not the control sequence disallows \.{\\par} +in its parameters. The |get_next| routine will set |long_state| to +|outer_call| and emit \.{\\par}, if a file ends or if an \.{\\outer} +control sequence occurs in the midst of an argument. + +@<Glob...@>= +static int @!long_state; /*governs the acceptance of \.{\\par}*/ + +@ The parameters, if any, must be scanned before the macro is expanded. +Parameters are token lists without reference counts. They are placed on +an auxiliary stack called |pstack| while they are being scanned, since +the |param_stack| may be losing entries during the matching process. +(Note that |param_stack| can't be gaining entries, since |macro_call| is +the only routine that puts anything onto |param_stack|, and it +is not recursive.) + +@<Glob...@>= +static pointer @!pstack[9]; /*arguments supplied to a macro*/ + +@ After parameter scanning is complete, the parameters are moved to the +|param_stack|. Then the macro body is fed to the scanner; in other words, +|macro_call| places the defined text of the control sequence at the +top of\/ \TeX's input stack, so that |get_next| will proceed to read it +next. + +The global variable |cur_cs| contains the |eqtb| address of the control sequence +being expanded, when |macro_call| begins. If this control sequence has not been +declared \.{\\long}, i.e., if its command code in the |eq_type| field is +not |long_call| or |long_outer_call|, its parameters are not allowed to contain +the control sequence \.{\\par}. If an illegal \.{\\par} appears, the macro +call is aborted, and the \.{\\par} will be rescanned. + +@<Declare the procedure called |macro_call|@>= +static void macro_call(void) /*invokes a user-defined control sequence*/ +{@+ +pointer r; /*current node in the macro's token list*/ +pointer @!p; /*current node in parameter token list being built*/ +pointer @!q; /*new node being put into the token list*/ +pointer @!s; /*backup pointer for parameter matching*/ +pointer @!t; /*cycle pointer for backup recovery*/ +pointer @!u, @!v; /*auxiliary pointers for backup recovery*/ +pointer @!rbrace_ptr; /*one step before the last |right_brace| token*/ +small_number @!n; /*the number of parameters scanned*/ +halfword @!unbalance; /*unmatched left braces in current parameter*/ +int @!m; /*the number of tokens or groups (usually)*/ +pointer @!ref_count; /*start of the token list*/ +small_number @!save_scanner_status; /*|scanner_status| upon entry*/ +pointer @!save_warning_index; /*|warning_index| upon entry*/ +ASCII_code @!match_chr; /*character used in parameter*/ +@<additional local variables for |macro_call|@>@; +save_scanner_status=scanner_status;save_warning_index=warning_index; +warning_index=cur_cs;ref_count=cur_chr;r=link(ref_count);n=0; +if (tracing_macros > 0) @<Show the text of the macro being expanded@>; +if (info(r)==protected_token) r=link(r); +if (info(r)!=end_match_token) + @<Scan the parameters and make |link(r)| point to the macro body; but |goto end| +if an illegal \.{\\par} is detected@>; +@<Feed the macro body and its parameters to the scanner@>; +end: scanner_status=save_scanner_status;warning_index=save_warning_index; +@<update the macro stack@>@; +} + +@ Before we put a new token list on the input stack, it is wise to clean off +all token lists that have recently been depleted. Then a user macro that ends +with a call to itself will not require unbounded stack space. + +@<Feed the macro body and its parameters to the scanner@>= +while ((state==token_list)&&(loc==null)&&(token_type!=v_template)) + end_token_list(); /*conserve stack space*/ +begin_token_list(ref_count, macro);name=warning_index;loc=link(r); +if (n > 0) + {@+if (param_ptr+n > max_param_stack) + {@+max_param_stack=param_ptr+n; + if (max_param_stack > param_size) + overflow("parameter stack size", param_size); +@:TeX capacity exceeded parameter stack size}{\quad parameter stack size@> + } + for (m=0; m<=n-1; m++) param_stack[param_ptr+m]=pstack[m]; + param_ptr=param_ptr+n; + } + +@ At this point, the reader will find it advisable to review the explanation +of token list format that was presented earlier, since many aspects of that +format are of importance chiefly in the |macro_call| routine. + +The token list might begin with a string of compulsory tokens before the +first |match| or |end_match|. In that case the macro name is supposed to be +followed by those tokens; the following program will set |s==null| to +represent this restriction. Otherwise |s| will be set to the first token of +a string that will delimit the next parameter. + +@<Scan the parameters and make |link(r)| point to the macro body...@>= +{@+scanner_status=matching;unbalance=0; +long_state=eq_type(cur_cs); +if (long_state >= outer_call) long_state=long_state-2; +@/do@+{link(temp_head)=null; +if ((info(r) > match_token+255)||(info(r) < match_token)) s=null; +else{@+match_chr=info(r)-match_token;s=link(r);r=s; + p=temp_head;m=0; + } +@<Scan a parameter until its delimiter string has been found; or, if |s=null|, +simply scan the delimiter string@>;@/ + /*now |info(r)| is a token whose command code is either |match| or |end_match|*/ +}@+ while (!(info(r)==end_match_token)); +} + +@ If |info(r)| is a |match| or |end_match| command, it cannot be equal to +any token found by |get_token|. Therefore an undelimited parameter---i.e., +a |match| that is immediately followed by |match| or |end_match|---will +always fail the test `|cur_tok==info(r)|' in the following algorithm. + +@<Scan a parameter until its delimiter string has been found; or,...@>= +resume: get_token(); /*set |cur_tok| to the next token of input*/ +if (cur_tok==info(r)) + @<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully +matched, otherwise |goto resume|@>; +@<Contribute the recently matched tokens to the current parameter, and |goto +resume| if a partial match is still in effect; but abort if |s=null|@>; +if (cur_tok==par_token) if (long_state!=long_call) + @<Report a runaway argument and abort@>; +if (cur_tok < right_brace_limit) + if (cur_tok < left_brace_limit) + @<Contribute an entire group to the current parameter@>@; + else@<Report an extra right brace and |goto resume|@>@; +else@<Store the current token, but |goto resume| if it is a blank space +that would become an undelimited parameter@>; +incr(m); +if (info(r) > end_match_token) goto resume; +if (info(r) < match_token) goto resume; +found: if (s!=null) @<Tidy up the parameter just scanned, and tuck it away@>@; + +@ @<Store the current token, but |goto resume| if it is...@>= +{@+if (cur_tok==space_token) + if (info(r) <= end_match_token) + if (info(r) >= match_token) goto resume; +store_new_token(cur_tok); +} + +@ A slightly subtle point arises here: When the parameter delimiter ends +with `\.{\#\{}', the token list will have a left brace both before and +after the |end_match|\kern-.4pt. Only one of these should affect the +|align_state|, but both will be scanned, so we must make a correction. + +@<Advance \(r)|r|; |goto found| if the parameter delimiter has been fully...@>= +{@+r=link(r); +if ((info(r) >= match_token)&&(info(r) <= end_match_token)) + {@+if (cur_tok < left_brace_limit) decr(align_state); + goto found; + } +else goto resume; +} + +@ @<Report an extra right brace and |goto resume|@>= +{@+back_input();print_err("Argument of ");sprint_cs(warning_index); +@.Argument of \\x has...@> +print(" has an extra }"); +help6("I've run across a `}' that doesn't seem to match anything.",@/ + "For example, `\\def\\a#1{...}' and `\\a}' would produce",@/ + "this error. If you simply proceed now, the `\\par' that",@/ + "I've just inserted will cause me to report a runaway",@/ + "argument that might be the root of the problem. But if",@/ + "your `}' was spurious, just type `2' and it will go away."); +incr(align_state);long_state=call;cur_tok=par_token;ins_error(); +goto resume; +} /*a white lie; the \.{\\par} won't always trigger a runaway*/ + +@ If |long_state==outer_call|, a runaway argument has already been reported. + +@<Report a runaway argument and abort@>= +{@+if (long_state==call) + {@+runaway();print_err("Paragraph ended before "); +@.Paragraph ended before...@> + sprint_cs(warning_index);print(" was complete"); + help3("I suspect you've forgotten a `}', causing me to apply this",@/ + "control sequence to too much text. How can we recover?",@/ + "My plan is to forget the whole thing and hope for the best."); + back_error(); + } +pstack[n]=link(temp_head);align_state=align_state-unbalance; +for (m=0; m<=n; m++) flush_list(pstack[m]); +goto end; +} + +@ When the following code becomes active, we have matched tokens from |s| to +the predecessor of |r|, and we have found that |cur_tok!=info(r)|. An +interesting situation now presents itself: If the parameter is to be +delimited by a string such as `\.{ab}', and if we have scanned `\.{aa}', +we want to contribute one `\.a' to the current parameter and resume +looking for a `\.b'. The program must account for such partial matches and +for others that can be quite complex. But most of the time we have |s==r| +and nothing needs to be done. + +Incidentally, it is possible for \.{\\par} tokens to sneak in to certain +parameters of non-\.{\\long} macros. For example, consider a case like +`\.{\\def\\a\#1\\par!\{...\}}' where the first \.{\\par} is not followed +by an exclamation point. In such situations it does not seem appropriate +to prohibit the \.{\\par}, so \TeX\ keeps quiet about this bending of +the rules. + +@<Contribute the recently matched tokens to the current parameter...@>= +if (s!=r) + if (s==null) @<Report an improper use of the macro and abort@>@; + else{@+t=s; + @/do@+{store_new_token(info(t));incr(m);u=link(t);v=s; + loop@+{@+if (u==r) + if (cur_tok!=info(v)) goto done; + else{@+r=link(v);goto resume; + } + if (info(u)!=info(v)) goto done; + u=link(u);v=link(v); + } + done: t=link(t); + }@+ while (!(t==r)); + r=s; /*at this point, no tokens are recently matched*/ + } + +@ @<Report an improper use...@>= +{@+print_err("Use of ");sprint_cs(warning_index); +@.Use of x doesn't match...@> +print(" doesn't match its definition"); +help4("If you say, e.g., `\\def\\a1{...}', then you must always",@/ + "put `1' after `\\a', since control sequence names are",@/ + "made up of letters only. The macro here has not been",@/ + "followed by the required stuff, so I'm ignoring it."); +error();goto end; +} + +@ @<Contribute an entire group to the current parameter@>= +{@+unbalance=1; +@^inner loop@> +loop@+{@+fast_store_new_token(cur_tok);get_token(); + if (cur_tok==par_token) if (long_state!=long_call) + @<Report a runaway argument and abort@>; + if (cur_tok < right_brace_limit) + if (cur_tok < left_brace_limit) incr(unbalance); + else{@+decr(unbalance); + if (unbalance==0) goto done1; + } + } +done1: rbrace_ptr=p;store_new_token(cur_tok); +} + +@ If the parameter consists of a single group enclosed in braces, we must +strip off the enclosing braces. That's why |rbrace_ptr| was introduced. + +@<Tidy up the parameter just scanned, and tuck it away@>= +{@+if ((m==1)&&(info(p) < right_brace_limit)) + {@+link(rbrace_ptr)=null;free_avail(p); + p=link(temp_head);pstack[n]=link(p);free_avail(p); + } +else pstack[n]=link(temp_head); +incr(n); +if (tracing_macros > 0) + {@+begin_diagnostic();print_nl("");printn(match_chr);print_int(n); + print("<-");show_token_list(pstack[n-1], null, 1000); + end_diagnostic(false); + } +} + +@ @<Show the text of the macro being expanded@>= +{@+begin_diagnostic();print_ln();print_cs(warning_index); +token_show(ref_count);end_diagnostic(false); +} + +@* Basic scanning subroutines. +Let's turn now to some procedures that \TeX\ calls upon frequently to digest +certain kinds of patterns in the input. Most of these are quite simple; +some are quite elaborate. Almost all of the routines call |get_x_token|, +which can cause them to be invoked recursively. +@^stomach@> +@^recursion@> + +@ The |scan_left_brace| routine is called when a left brace is supposed to be +the next non-blank token. (The term ``left brace'' means, more precisely, +a character whose catcode is |left_brace|.) \TeX\ allows \.{\\relax} to +appear before the |left_brace|. + +@p static void scan_left_brace(void) /*reads a mandatory |left_brace|*/ +{@+@<Get the next non-blank non-relax non-call token@>; +if (cur_cmd!=left_brace) + {@+print_err("Missing { inserted"); +@.Missing \{ inserted@> + help4("A left brace was mandatory here, so I've put one in.",@/ + "You might want to delete and/or insert some corrections",@/ + "so that I will find a matching right brace soon.",@/ + "(If you're confused by all this, try typing `I}' now.)"); + back_error();cur_tok=left_brace_token+'{';cur_cmd=left_brace; + cur_chr='{';incr(align_state); + } +} + +@ @<Get the next non-blank non-relax non-call token@>= +@/do@+{get_x_token(); +}@+ while (!((cur_cmd!=spacer)&&(cur_cmd!=relax))) + +@ The |scan_optional_equals| routine looks for an optional `\.=' sign preceded +by optional spaces; `\.{\\relax}' is not ignored here. + +@p static void scan_optional_equals(void) +{@+@<Get the next non-blank non-call token@>; +if (cur_tok!=other_token+'=') back_input(); +} + +@ @<Get the next non-blank non-call token@>= +@/do@+{get_x_token(); +}@+ while (!(cur_cmd!=spacer)) + +@ In case you are getting bored, here is a slightly less trivial routine: +Given a string of lowercase letters, like `\.{pt}' or `\.{plus}' or +`\.{width}', the |scan_keyword| routine checks to see whether the next +tokens of input match this string. The match must be exact, except that +uppercase letters will match their lowercase counterparts; uppercase +equivalents are determined by subtracting |'a'-'A'|, rather than using the +|uc_code| table, since \TeX\ uses this routine only for its own limited +set of keywords. + +If a match is found, the characters are effectively removed from the input +and |true| is returned. Otherwise |false| is returned, and the input +is left essentially unchanged (except for the fact that some macros +may have been expanded, etc.). +@^inner loop@> + +@p static bool scan_keyword(char *@!s) /*look for a given string*/ +{@+ +pointer p; /*tail of the backup list*/ +pointer @!q; /*new node being added to the token list via |store_new_token|*/ +p=backup_head;link(p)=null; +while (*s!=0) + {@+get_x_token(); /*recursion is possible here*/ +@^recursion@> + if ((cur_cs==0)&&@| + ((cur_chr==so(*s))||(cur_chr==so(*s)-'a'+'A'))) + {@+store_new_token(cur_tok);incr(s); + } + else if ((cur_cmd!=spacer)||(p!=backup_head)) + {@+back_input(); + if (p!=backup_head) back_list(link(backup_head)); + return false; + } + } +flush_list(link(backup_head));return true; +} + +@ Here is a procedure that sounds an alarm when mu and non-mu units +are being switched. + +@p static void mu_error(void) +{@+print_err("Incompatible glue units"); +@.Incompatible glue units@> +help1("I'm going to assume that 1mu=1pt when they're mixed."); +error(); +} + +@ The next routine `|scan_something_internal|' is used to fetch internal +numeric quantities like `\.{\\hsize}', and also to handle the `\.{\\the}' +when expanding constructions like `\.{\\the\\toks0}' and +`\.{\\the\\baselineskip}'. Soon we will be considering the |scan_int| +procedure, which calls |scan_something_internal|; on the other hand, +|scan_something_internal| also calls |scan_int|, for constructions like +`\.{\\catcode\`\\\$}' or `\.{\\fontdimen} \.3 \.{\\ff}'. So we +have to declare |scan_int| as a |forward| procedure. A few other +procedures are also declared at this point. + +@p static void scan_int(void); /*scans an integer value*/ +@t\4\4@>@<Declare procedures that scan restricted classes of integers@>@; +@t\4\4@>@<Declare \eTeX\ procedures for scanning@>@; +@t\4\4@>@<Declare procedures that scan font-related stuff@>@; + +@ \TeX\ doesn't know exactly what to expect when |scan_something_internal| +begins. For example, an integer or dimension or glue value could occur +immediately after `\.{\\hskip}'; and one can even say \.{\\the} with +respect to token lists in constructions like +`\.{\\xdef\\o\{\\the\\output\}}'. On the other hand, only integers are +allowed after a construction like `\.{\\count}'. To handle the various +possibilities, |scan_something_internal| has a |level| parameter, which +tells the ``highest'' kind of quantity that |scan_something_internal| is +allowed to produce. Six levels are distinguished, namely |int_val|, +|dimen_val|, |glue_val|, |mu_val|, |ident_val|, and |tok_val|. + +The output of |scan_something_internal| (and of the other routines +|scan_int|, |scan_dimen|, and |scan_glue| below) is put into the global +variable |cur_val|, and its level is put into |cur_val_level|. The highest +values of |cur_val_level| are special: |mu_val| is used only when +|cur_val| points to something in a ``muskip'' register, or to one of the +three parameters \.{\\thinmuskip}, \.{\\medmuskip}, \.{\\thickmuskip}; +|ident_val| is used only when |cur_val| points to a font identifier; +|tok_val| is used only when |cur_val| points to |null| or to the reference +count of a token list. The last two cases are allowed only when +|scan_something_internal| is called with |level==tok_val|. + +If the output is glue, |cur_val| will point to a glue specification, and +the reference count of that glue will have been updated to reflect this +reference; if the output is a nonempty token list, |cur_val| will point to +its reference count, but in this case the count will not have been updated. +Otherwise |cur_val| will contain the integer or scaled value in question. + +@d int_val 0 /*integer values*/ +@d dimen_val 1 /*dimension values*/ +@d glue_val 2 /*glue specifications*/ +@d mu_val 3 /*math glue specifications*/ +@d ident_val 4 /*font identifier*/ +@d tok_val 5 /*token lists*/ + +@<Glob...@>= +static int @!cur_val; /*value returned by numeric scanners*/ +static int @!cur_val_level; /*the ``level'' of this value*/ + +@ The hash table is initialized with `\.{\\count}', `\.{\\dimen}', `\.{\\skip}', +and `\.{\\muskip}' all having |internal_register| as their command code; they are +distinguished by the |chr_code|, which is either |int_val|, |dimen_val|, +|glue_val|, or |mu_val| more than |mem_bot| (dynamic variable-size nodes +cannot have these values) + +@<Put each...@>= +primitive("count", internal_register, mem_bot+int_val); +@!@:count\_}{\.{\\count} primitive@> +primitive("dimen", internal_register, mem_bot+dimen_val); +@!@:dimen\_}{\.{\\dimen} primitive@> +primitive("skip", internal_register, mem_bot+glue_val); +@!@:skip\_}{\.{\\skip} primitive@> +primitive("muskip", internal_register, mem_bot+mu_val); +@!@:mu\_skip\_}{\.{\\muskip} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case internal_register: @<Cases of |register| for |print_cmd_chr|@>@;@+break; + +@ OK, we're ready for |scan_something_internal| itself. A second parameter, +|negative|, is set |true| if the value that is found should be negated. +It is assumed that |cur_cmd| and |cur_chr| represent the first token of +the internal quantity to be scanned; an error will be signalled if +|cur_cmd < min_internal| or |cur_cmd > max_internal|. + +@d scanned_result(A, B) @+{@+cur_val=A;cur_val_level=B;@+} + +@p static void scan_something_internal(small_number @!level, bool @!negative) + /*fetch an internal parameter*/ +{@+ +halfword m; /*|chr_code| part of the operand token*/ +pointer @!q, @!r; /*general purpose indices*/ +pointer @!tx; /*effective tail node*/ +four_quarters @!i; /*character info*/ +int @!p; /*index into |nest|*/ +m=cur_chr; +switch (cur_cmd) { +case def_code: @<Fetch a character code from some table@>@;@+break; +case toks_register: case assign_toks: + case def_family: case set_font: case def_font: @<Fetch a token list or font +identifier, provided that |level=tok_val|@>@;@+break; +case assign_int: scanned_result(eqtb[m].i, int_val)@;@+break; +case assign_dimen: scanned_result(eqtb[m].sc, dimen_val)@;@+break; +case assign_glue: scanned_result(equiv(m), glue_val)@;@+break; +case assign_mu_glue: scanned_result(equiv(m), mu_val)@;@+break; +case set_aux: @<Fetch the |space_factor| or the |prev_depth|@>@;@+break; +case set_prev_graf: @<Fetch the |prev_graf|@>@;@+break; +case set_page_int: @<Fetch the |dead_cycles| or the |insert_penalties|@>@;@+break; +case set_page_dimen: @<Fetch something on the |page_so_far|@>@;@+break; +case set_shape: @<Fetch the |par_shape| size@>@;@+break; +case set_box_dimen: @<Fetch a box dimension@>@;@+break; +case char_given: case math_given: scanned_result(cur_chr, int_val)@;@+break; +case assign_font_dimen: @<Fetch a font dimension@>@;@+break; +case assign_font_int: @<Fetch a font integer@>@;@+break; +case internal_register: @<Fetch a register@>@;@+break; +case last_item: @<Fetch an item in the current node, if appropriate@>@;@+break; +default:@<Complain that \.{\\the} can't do this; give zero result@>@; +} @/ +while (cur_val_level > level) @<Convert \(c)|cur_val| to a lower level@>; +@<Fix the reference count, if any, and negate |cur_val| if |negative|@>; +} + +@ @<Fetch a character code from some table@>= +{@+scan_char_num(); +if (m==math_code_base) scanned_result(ho(math_code(cur_val)), int_val)@; +else if (m < math_code_base) scanned_result(equiv(m+cur_val), int_val)@; +else scanned_result(eqtb[m+cur_val].i, int_val); +} + +@ @<Fetch a token list...@>= +if (level!=tok_val) + {@+print_err("Missing number, treated as zero"); +@.Missing number...@> + help3("A number should have been here; I inserted `0'.",@/ + "(If you can't figure out why I needed to see a number,",@/ + "look up `weird error' in the index to The TeXbook.)"); +@:TeXbook}{\sl The \TeX book@> + back_error();scanned_result(0, dimen_val); + } +else if (cur_cmd <= assign_toks) + {@+if (cur_cmd < assign_toks) /*|cur_cmd==toks_register|*/ + if (m==mem_bot) + {@+scan_register_num(); + if (cur_val < 256) cur_val=equiv(toks_base+cur_val); + else{@+find_sa_element(tok_val, cur_val, false); + if (cur_ptr==null) cur_val=null; + else cur_val=sa_ptr(cur_ptr); + } + } + else cur_val=sa_ptr(m); + else cur_val=equiv(m); + cur_val_level=tok_val; + } +else{@+back_input();scan_font_ident(); + scanned_result(font_id_base+cur_val, ident_val); + } + +@ Users refer to `\.{\\the\\spacefactor}' only in horizontal +mode, and to `\.{\\the\\prevdepth}' only in vertical mode; so we put the +associated mode in the modifier part of the |set_aux| command. +The |set_page_int| command has modifier 0 or 1, for `\.{\\deadcycles}' and +`\.{\\insertpenalties}', respectively. The |set_box_dimen| command is +modified by either |width_offset|, |height_offset|, or |depth_offset|. +And the |last_item| command is modified by either |int_val|, |dimen_val|, +|glue_val|, |input_line_no_code|, or |badness_code|. +\eTeX\ inserts |last_node_type_code| after |glue_val| and adds +the codes for its extensions: |eTeX_version_code|, \dots\ . + +@d last_node_type_code (glue_val+1) /*code for \.{\\lastnodetype}*/ +@d input_line_no_code (glue_val+2) /*code for \.{\\inputlineno}*/ +@d badness_code (input_line_no_code+1) /*code for \.{\\badness}*/ +@# +@d eTeX_int (pdftex_last_item_codes+1) /*first of \eTeX\ codes for integers*/ +@d eTeX_dim (eTeX_int+8) /*first of \eTeX\ codes for dimensions*/ +@d eTeX_glue (eTeX_dim+9) /*first of \eTeX\ codes for glue*/ +@d eTeX_mu (eTeX_glue+1) /*first of \eTeX\ codes for muglue*/ +@d eTeX_expr (eTeX_mu+1) /*first of \eTeX\ codes for expressions*/ +@d eTeX_last_last_item_cmd_mod (eTeX_expr-int_val+mu_val) /*\.{\\muexpr}*/ + +@<Put each...@>= +primitive("spacefactor", set_aux, hmode); +@!@:space\_factor\_}{\.{\\spacefactor} primitive@> +primitive("prevdepth", set_aux, vmode);@/ +@!@:prev\_depth\_}{\.{\\prevdepth} primitive@> +primitive("deadcycles", set_page_int, 0); +@!@:dead\_cycles\_}{\.{\\deadcycles} primitive@> +primitive("insertpenalties", set_page_int, 1); +@!@:insert\_penalties\_}{\.{\\insertpenalties} primitive@> +primitive("wd", set_box_dimen, width_offset); +@!@:wd\_}{\.{\\wd} primitive@> +primitive("ht", set_box_dimen, height_offset); +@!@:ht\_}{\.{\\ht} primitive@> +primitive("dp", set_box_dimen, depth_offset); +@!@:dp\_}{\.{\\dp} primitive@> +primitive("lastpenalty", last_item, int_val); +@!@:last\_penalty\_}{\.{\\lastpenalty} primitive@> +primitive("lastkern", last_item, dimen_val); +@!@:last\_kern\_}{\.{\\lastkern} primitive@> +primitive("lastskip", last_item, glue_val); +@!@:last\_skip\_}{\.{\\lastskip} primitive@> +primitive("inputlineno", last_item, input_line_no_code); +@!@:input\_line\_no\_}{\.{\\inputlineno} primitive@> +primitive("badness", last_item, badness_code); +@!@:badness\_}{\.{\\badness} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case set_aux: if (chr_code==vmode) print_esc("prevdepth"); +@+else print_esc("spacefactor");@+break; +case set_page_int: if (chr_code==0) print_esc("deadcycles") +@/@<Cases of |set_page_int| for |print_cmd_chr|@>;@/ +@+else print_esc("insertpenalties");@+break; +case set_box_dimen: if (chr_code==width_offset) print_esc("wd"); +else if (chr_code==height_offset) print_esc("ht"); +else print_esc("dp");@+break; +case last_item: switch (chr_code) { + case int_val: print_esc("lastpenalty");@+break; + case dimen_val: print_esc("lastkern");@+break; + case glue_val: print_esc("lastskip");@+break; + case input_line_no_code: print_esc("inputlineno");@+break; + @/@<Cases of |last_item| for |print_cmd_chr|@>@/ + default:print_esc("badness"); + } @+break; + +@ @<Fetch the |space_factor| or the |prev_depth|@>= +if (abs(mode)!=m) + {@+print_err("Improper ");print_cmd_chr(set_aux, m); +@.Improper \\spacefactor@> +@.Improper \\prevdepth@> + help4("You can refer to \\spacefactor only in horizontal mode;",@/ + "you can refer to \\prevdepth only in vertical mode; and",@/ + "neither of these is meaningful inside \\write. So",@/ + "I'm forgetting what you said and using zero instead."); + error(); + if (level!=tok_val) scanned_result(0, dimen_val)@; + else scanned_result(0, int_val); + } +else if (m==vmode) scanned_result(prev_depth, dimen_val)@; +else scanned_result(space_factor, int_val) + +@ @<Fetch the |dead_cycles| or the |insert_penalties|@>= +{@+if (m==0) cur_val=dead_cycles +@/@<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>;@/ +else cur_val=insert_penalties; +cur_val_level=int_val; +} + +@ @<Fetch a box dimension@>= +{@+scan_register_num();fetch_box(q); +if (q==null) cur_val=0;@+else cur_val=mem[q+m].sc; +cur_val_level=dimen_val; +} + +@ Inside an \.{\\output} routine, a user may wish to look at the page totals +that were present at the moment when output was triggered. + +@d max_dimen 07777777777 /*$2^{30}-1$*/ + +@<Fetch something on the |page_so_far|@>= +{@+if ((page_contents==empty)&&(!output_active)) + if (m==0) cur_val=max_dimen;@+else cur_val=0; +else cur_val=page_so_far[m]; +cur_val_level=dimen_val; +} + +@ @<Fetch the |prev_graf|@>= +if (mode==0) scanned_result(0, int_val)@; /*|prev_graf==0| within \.{\\write}*/ +else{@+nest[nest_ptr]=cur_list;p=nest_ptr; + while (abs(nest[p].mode_field)!=vmode) decr(p); + scanned_result(nest[p].pg_field, int_val); + } + +@ @<Fetch the |par_shape| size@>= +{@+if (m > par_shape_loc) @<Fetch a penalties array element@>@; +else if (par_shape_ptr==null) cur_val=0; +else cur_val=info(par_shape_ptr); +cur_val_level=int_val; +} + +@ Here is where \.{\\lastpenalty}, \.{\\lastkern}, \.{\\lastskip}, and +\.{\\lastnodetype} are +implemented. The reference count for \.{\\lastskip} will be updated later. + +We also handle \.{\\inputlineno} and \.{\\badness} here, because they are +legal in similar contexts. + +@<Fetch an item in the current node...@>= +if (m > eTeX_last_last_item_cmd_mod) +{@+switch (m) { + @/@<Cases for fetching a \Prote\ int value@>@/ + } /*there are no other cases*/ + cur_val_level=int_val; +} +else if (m >= input_line_no_code) +{if (m >= eTeX_glue) @<Process an expression and |return|@>@; + else if (m >= eTeX_dim) + {@+switch (m) { + @/@<Cases for fetching a dimension value@>@/ + } /*there are no other cases*/ + cur_val_level=dimen_val; + } + else + {@+switch (m) { + case input_line_no_code: cur_val=line;@+break; + case badness_code: cur_val=last_badness;@+break; + @/@<Cases for fetching an integer value@>@/ + } /*there are no other cases*/ + cur_val_level=int_val; + } +} +else{@+if (cur_chr==glue_val) cur_val=zero_glue;@+else cur_val=0; + tx=tail; + if (cur_chr==last_node_type_code) + {@+cur_val_level=int_val; + if ((tx==head)||(mode==0)) cur_val=-1; + } + else cur_val_level=cur_chr; + if (!is_char_node(tx)&&(mode!=0)) + switch (cur_chr) { + case int_val: if (type(tx)==penalty_node) cur_val=penalty(tx);@+break; + case dimen_val: if (type(tx)==kern_node) cur_val=width(tx);@+break; + case glue_val: if (type(tx)==glue_node) + {@+cur_val=glue_ptr(tx); + if (subtype(tx)==mu_glue) cur_val_level=mu_val; + } @+break; + case last_node_type_code: if (type(tx) <= unset_node) cur_val=type(tx)+1; + else cur_val=unset_node+2; + } /*there are no other cases*/ + else if ((mode==vmode)&&(tx==head)) + switch (cur_chr) { + case int_val: cur_val=last_penalty;@+break; + case dimen_val: cur_val=last_kern;@+break; + case glue_val: if (last_glue!=max_halfword) cur_val=last_glue;@+break; + case last_node_type_code: cur_val=last_node_type; + } /*there are no other cases*/ + } + +@ @<Fetch a font dimension@>= +{@+find_font_dimen(false);font_info[fmem_ptr].sc=0; +scanned_result(font_info[cur_val].sc, dimen_val); +} + +@ @<Fetch a font integer@>= +{@+scan_font_ident(); +if (m==0) scanned_result(hyphen_char[cur_val], int_val)@; +else scanned_result(skew_char[cur_val], int_val); +} + +@ @<Fetch a register@>= +{@+if ((m < mem_bot)||(m > lo_mem_stat_max)) + {@+cur_val_level=sa_type(m); + if (cur_val_level < glue_val) cur_val=sa_int(m); + else cur_val=sa_ptr(m); + } +else{@+scan_register_num();cur_val_level=m-mem_bot; + if (cur_val > 255) + {@+find_sa_element(cur_val_level, cur_val, false); + if (cur_ptr==null) + if (cur_val_level < glue_val) cur_val=0; + else cur_val=zero_glue; + else if (cur_val_level < glue_val) cur_val=sa_int(cur_ptr); + else cur_val=sa_ptr(cur_ptr); + } + else + switch (cur_val_level) { +case int_val: cur_val=count(cur_val);@+break; +case dimen_val: cur_val=dimen(cur_val);@+break; +case glue_val: cur_val=skip(cur_val);@+break; +case mu_val: cur_val=mu_skip(cur_val); +} /*there are no other cases*/ + } +} + +@ @<Complain that \.{\\the} can't do this; give zero result@>= +{@+print_err("You can't use `");print_cmd_chr(cur_cmd, cur_chr); +@.You can't use x after ...@> +print("' after ");print_esc("the"); +help1("I'm forgetting what you said and using zero instead."); +error(); +if (level!=tok_val) scanned_result(0, dimen_val)@; +else scanned_result(0, int_val); +} + +@ When a |glue_val| changes to a |dimen_val|, we use the width component +of the glue; there is no need to decrease the reference count, since it +has not yet been increased. When a |dimen_val| changes to an |int_val|, +we use scaled points so that the value doesn't actually change. And when a +|mu_val| changes to a |glue_val|, the value doesn't change either. + +@<Convert \(c)|cur_val| to a lower level@>= +{@+if (cur_val_level==glue_val) cur_val=width(cur_val); +else if (cur_val_level==mu_val) mu_error(); +decr(cur_val_level); +} + +@ If |cur_val| points to a glue specification at this point, the reference +count for the glue does not yet include the reference by |cur_val|. +If |negative| is |true|, |cur_val_level| is known to be | <= mu_val|. + +@<Fix the reference count, if any,...@>= +if (negative) + if (cur_val_level >= glue_val) + {@+cur_val=new_spec(cur_val); + @<Negate all three glue components of |cur_val|@>; + } + else negate(cur_val); +else if ((cur_val_level >= glue_val)&&(cur_val_level <= mu_val)) + add_glue_ref(cur_val) + +@ @<Negate all three...@>= +{@+negate(width(cur_val)); +negate(stretch(cur_val)); +negate(shrink(cur_val)); +} + +@ Our next goal is to write the |scan_int| procedure, which scans anything that +\TeX\ treats as an integer. But first we might as well look at some simple +applications of |scan_int| that have already been made inside of +|scan_something_internal|. + +@ @<Declare procedures that scan restricted classes of integers@>= +static void scan_eight_bit_int(void) +{@+scan_int(); +if ((cur_val < 0)||(cur_val > 255)) + {@+print_err("Bad register code"); +@.Bad register code@> + help2("A register number must be between 0 and 255.",@/ + "I changed this one to zero.");int_error(cur_val);cur_val=0; + } +} + +@ @<Declare procedures that scan restricted classes of integers@>= +static void scan_char_num(void) +{@+scan_int(); +if ((cur_val < 0)||(cur_val > 255)) + {@+print_err("Bad character code"); +@.Bad character code@> + help2("A character number must be between 0 and 255.",@/ + "I changed this one to zero.");int_error(cur_val);cur_val=0; + } +} + +@ While we're at it, we might as well deal with similar routines that +will be needed later. + +@<Declare procedures that scan restricted classes of integers@>= +static void scan_four_bit_int(void) +{@+scan_int(); +if ((cur_val < 0)||(cur_val > 15)) + {@+print_err("Bad number"); +@.Bad number@> + help2("Since I expected to read a number between 0 and 15,",@/ + "I changed this one to zero.");int_error(cur_val);cur_val=0; + } +} + +@ @<Declare procedures that scan restricted classes of integers@>= +static void scan_fifteen_bit_int(void) +{@+scan_int(); +if ((cur_val < 0)||(cur_val > 077777)) + {@+print_err("Bad mathchar"); +@.Bad mathchar@> + help2("A mathchar number must be between 0 and 32767.",@/ + "I changed this one to zero.");int_error(cur_val);cur_val=0; + } +} + +@ @<Declare procedures that scan restricted classes of integers@>= +static void scan_twenty_seven_bit_int(void) +{@+scan_int(); +if ((cur_val < 0)||(cur_val > 0777777777)) + {@+print_err("Bad delimiter code"); +@.Bad delimiter code@> + help2("A numeric delimiter code must be between 0 and 2^{27}-1.",@/ + "I changed this one to zero.");int_error(cur_val);cur_val=0; + } +} + +@ An integer number can be preceded by any number of spaces and `\.+' or +`\.-' signs. Then comes either a decimal constant (i.e., radix 10), an +octal constant (i.e., radix 8, preceded by~\.\'), a hexadecimal constant +(radix 16, preceded by~\."), an alphabetic constant (preceded by~\.\`), or +an internal variable. After scanning is complete, +|cur_val| will contain the answer, which must be at most +$2^{31}-1=2147483647$ in absolute value. The value of |radix| is set to +10, 8, or 16 in the cases of decimal, octal, or hexadecimal constants, +otherwise |radix| is set to zero. An optional space follows a constant. + +@d octal_token (other_token+'\'') /*apostrophe, indicates an octal constant*/ +@d hex_token (other_token+'"') /*double quote, indicates a hex constant*/ +@d alpha_token (other_token+'`') /*reverse apostrophe, precedes alpha constants*/ +@d point_token (other_token+'.') /*decimal point*/ +@d continental_point_token (other_token+',') /*decimal point, Eurostyle*/ + +@<Glob...@>= +static small_number @!radix; /*|scan_int| sets this to 8, 10, 16, or zero*/ + +@ We initialize the following global variables just in case |expand| +comes into action before any of the basic scanning routines has assigned +them a value. + +@<Set init...@>= +cur_val=0;cur_val_level=int_val;radix=0;cur_order=normal; + +@ The |scan_int| routine is used also to scan the integer part of a +fraction; for example, the `\.3' in `\.{3.14159}' will be found by +|scan_int|. The |scan_dimen| routine assumes that |cur_tok==point_token| +after the integer part of such a fraction has been scanned by |scan_int|, +and that the decimal point has been backed up to be scanned again. + +@p static void scan_int(void) /*sets |cur_val| to an integer*/ +{@+ +bool negative; /*should the answer be negated?*/ +int @!m; /*|@t$2^{31}$@>/radix|, the threshold of danger*/ +small_number @!d; /*the digit just scanned*/ +bool @!vacuous; /*have no digits appeared?*/ +bool @!OK_so_far; /*has an error message been issued?*/ +radix=0;OK_so_far=true;@/ +@<Get the next non-blank non-sign token; set |negative| appropriately@>; +if (cur_tok==alpha_token) @<Scan an alphabetic character code into |cur_val|@>@; +else if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal)) + scan_something_internal(int_val, false); +else@<Scan a numeric constant@>; +if (negative) negate(cur_val); +} + +@ @<Get the next non-blank non-sign token...@>= +negative=false; +@/do@+{@<Get the next non-blank non-call token@>; +if (cur_tok==other_token+'-') + {@+negative=!negative;cur_tok=other_token+'+'; + } +}@+ while (!(cur_tok!=other_token+'+')) + +@ A space is ignored after an alphabetic character constant, so that +such constants behave like numeric ones. + +@<Scan an alphabetic character code into |cur_val|@>= +{@+get_token(); /*suppress macro expansion*/ +if (cur_tok < cs_token_flag) + {@+cur_val=cur_chr; + if (cur_cmd <= right_brace) + if (cur_cmd==right_brace) incr(align_state); + else decr(align_state); + } +else if (cur_tok < cs_token_flag+single_base) + cur_val=cur_tok-cs_token_flag-active_base; +else cur_val=cur_tok-cs_token_flag-single_base; +if (cur_val > 255) + {@+print_err("Improper alphabetic constant"); +@.Improper alphabetic constant@> + help2("A one-character control sequence belongs after a ` mark.",@/ + "So I'm essentially inserting \\0 here."); + cur_val='0';back_error(); + } +else@<Scan an optional space@>; +} + +@ @<Scan an optional space@>= +{@+get_x_token();if (cur_cmd!=spacer) back_input(); +} + +@ @<Scan a numeric constant@>= +{@+radix=10;m=214748364; +if (cur_tok==octal_token) + {@+radix=8;m=02000000000;get_x_token(); + } +else if (cur_tok==hex_token) + {@+radix=16;m=01000000000;get_x_token(); + } +vacuous=true;cur_val=0;@/ +@<Accumulate the constant until |cur_tok| is not a suitable digit@>; +if (vacuous) @<Express astonishment that no number was here@>@; +else if (cur_cmd!=spacer) back_input(); +} + +@ @d infinity 017777777777 /*the largest positive value that \TeX\ knows*/ +@d zero_token (other_token+'0') /*zero, the smallest digit*/ +@d A_token (letter_token+'A') /*the smallest special hex digit*/ +@d other_A_token (other_token+'A') /*special hex digit of type |other_char|*/ + +@<Accumulate the constant...@>= +loop@+{@+if ((cur_tok < zero_token+radix)&&(cur_tok >= zero_token)&& + (cur_tok <= zero_token+9)) d=cur_tok-zero_token; + else if (radix==16) + if ((cur_tok <= A_token+5)&&(cur_tok >= A_token)) d=cur_tok-A_token+10; + else if ((cur_tok <= other_A_token+5)&&(cur_tok >= other_A_token)) + d=cur_tok-other_A_token+10; + else goto done; + else goto done; + vacuous=false; + if ((cur_val >= m)&&((cur_val > m)||(d > 7)||(radix!=10))) + {@+if (OK_so_far) + {@+print_err("Number too big"); +@.Number too big@> + help2("I can only go up to 2147483647='17777777777=\"7FFFFFFF,",@/ + "so I'm using that number instead of yours."); + error();cur_val=infinity;OK_so_far=false; + } + } + else cur_val=cur_val*radix+d; + get_x_token(); + } +done: + +@ @<Express astonishment...@>= +{@+print_err("Missing number, treated as zero"); +@.Missing number...@> +help3("A number should have been here; I inserted `0'.",@/ + "(If you can't figure out why I needed to see a number,",@/ + "look up `weird error' in the index to The TeXbook.)"); +@:TeXbook}{\sl The \TeX book@> +back_error(); +} + +@ The |scan_dimen| routine is similar to |scan_int|, but it sets |cur_val| to +a |scaled| value, i.e., an integral number of sp. One of its main tasks +is therefore to interpret the abbreviations for various kinds of units and +to convert measurements to scaled points. + +There are three parameters: |mu| is |true| if the finite units must be +`\.{mu}', while |mu| is |false| if `\.{mu}' units are disallowed; +|inf| is |true| if the infinite units `\.{fil}', `\.{fill}', `\.{filll}' +are permitted; and |shortcut| is |true| if |cur_val| already contains +an integer and only the units need to be considered. + +The order of infinity that was found in the case of infinite glue is returned +in the global variable |cur_order|. + +@<Glob...@>= +static glue_ord @!cur_order; /*order of infinity found by |scan_dimen|*/ + +@ Constructions like `\.{-\'77 pt}' are legal dimensions, so |scan_dimen| +may begin with |scan_int|. This explains why it is convenient to use +|scan_int| also for the integer part of a decimal fraction. + +Several branches of |scan_dimen| work with |cur_val| as an integer and +with an auxiliary fraction |f|, so that the actual quantity of interest is +$|cur_val|+|f|/2^{16}$. At the end of the routine, this ``unpacked'' +representation is put into the single word |cur_val|, which suddenly +switches significance from |int| to |scaled|. + +@d scan_normal_dimen scan_dimen(false, false, false) + +@p static void scan_dimen(bool @!mu, bool @!inf, bool @!shortcut) + /*sets |cur_val| to a dimension*/ +{@+ +bool negative; /*should the answer be negated?*/ +int @!f; /*numerator of a fraction whose denominator is $2^{16}$*/ +@<Local variables for dimension calculations@>@; +f=0;arith_error=false;cur_order=normal;negative=false; +if (!shortcut) + {@+@<Get the next non-blank non-sign...@>; + if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal)) + @<Fetch an internal dimension and |goto attach_sign|, or fetch an internal +integer@>@; + else{@+back_input(); + if (cur_tok==continental_point_token) cur_tok=point_token; + if (cur_tok!=point_token) scan_int(); + else{@+radix=10;cur_val=0; + } + if (cur_tok==continental_point_token) cur_tok=point_token; + if ((radix==10)&&(cur_tok==point_token)) @<Scan decimal fraction@>; + } + } +if (cur_val < 0) /*in this case |f==0|*/ + {@+negative=!negative;negate(cur_val); + } +@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$, where there +are |x| sp per unit; |goto attach_sign| if the units are internal@>; +@<Scan an optional space@>; +attach_sign: if (arith_error||(abs(cur_val) >= 010000000000)) + @<Report that this dimension is out of range@>; +if (negative) negate(cur_val); +} + +@ @<Fetch an internal dimension and |goto attach_sign|...@>= +if (mu) + {@+scan_something_internal(mu_val, false); + @<Coerce glue to a dimension@>; + if (cur_val_level==mu_val) goto attach_sign; + if (cur_val_level!=int_val) mu_error(); + } +else{@+scan_something_internal(dimen_val, false); + if (cur_val_level==dimen_val) goto attach_sign; + } + +@ @<Local variables for dimension calculations@>= +int @!num, @!denom; /*conversion ratio for the scanned units*/ +int @!k, @!kk; /*number of digits in a decimal fraction*/ +pointer @!p, @!q; /*top of decimal digit stack*/ +scaled @!v; /*an internal dimension*/ +int @!save_cur_val; /*temporary storage of |cur_val|*/ + +@ The following code is executed when |scan_something_internal| was +called asking for |mu_val|, when we really wanted a ``mudimen'' instead +of ``muglue.'' + +@<Coerce glue to a dimension@>= +if (cur_val_level >= glue_val) + {@+v=width(cur_val);delete_glue_ref(cur_val);cur_val=v; + } + +@ When the following code is executed, we have |cur_tok==point_token|, but this +token has been backed up using |back_input|; we must first discard it. + +It turns out that a decimal point all by itself is equivalent to `\.{0.0}'. +Let's hope people don't use that fact. + +@<Scan decimal fraction@>= +{@+k=0;p=null;get_token(); /*|point_token| is being re-scanned*/ +loop@+{@+get_x_token(); + if ((cur_tok > zero_token+9)||(cur_tok < zero_token)) goto done1; + if (k < 17) /*digits for |k >= 17| cannot affect the result*/ + {@+q=get_avail();link(q)=p;info(q)=cur_tok-zero_token; + p=q;incr(k); + } + } +done1: for (kk=k; kk>=1; kk--) + {@+dig[kk-1]=info(p);q=p;p=link(p);free_avail(q); + } +f=round_decimals(k); +if (cur_cmd!=spacer) back_input(); +} + +@ Now comes the harder part: At this point in the program, |cur_val| is a +nonnegative integer and $f/2^{16}$ is a nonnegative fraction less than 1; +we want to multiply the sum of these two quantities by the appropriate +factor, based on the specified units, in order to produce a |scaled| +result, and we want to do the calculation with fixed point arithmetic that +does not overflow. + +@<Scan units and set |cur_val| to $x\cdot(|cur_val|+f/2^{16})$...@>= +if (inf) @<Scan for \(f)\.{fil} units; |goto attach_fraction| if found@>; +@<Scan for \(u)units that are internal dimensions; |goto attach_sign| with +|cur_val| set if found@>; +if (mu) @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>; +if (scan_keyword("true")) @<Adjust \(f)for the magnification ratio@>; +@.true@> +if (scan_keyword("pt")) goto attach_fraction; /*the easy case*/ +@.pt@> +@<Scan for \(a)all other units and adjust |cur_val| and |f| accordingly; |goto +done| in the case of scaled points@>; +attach_fraction: if (cur_val >= 040000) arith_error=true; +else cur_val=cur_val*unity+f; +done: + +@ A specification like `\.{filllll}' or `\.{fill L L L}' will lead to two +error messages (one for each additional keyword \.{"l"}). + +@<Scan for \(f)\.{fil} units...@>= +if (scan_keyword("fil")) +@.fil@> + {@+cur_order=fil; + while (scan_keyword("l")) + {@+if (cur_order==filll) + {@+print_err("Illegal unit of measure ("); +@.Illegal unit of measure@> + print("replaced by filll)"); + help1("I dddon't go any higher than filll.");error(); + } + else incr(cur_order); + } + goto attach_fraction; + } + +@ @<Scan for \(u)units that are internal dimensions...@>= +save_cur_val=cur_val; +@<Get the next non-blank non-call...@>; +if ((cur_cmd < min_internal)||(cur_cmd > max_internal)) back_input(); +else{@+if (mu) + {@+scan_something_internal(mu_val, false);@<Coerce glue...@>; + if (cur_val_level!=mu_val) mu_error(); + } + else scan_something_internal(dimen_val, false); + v=cur_val;goto found; + } +if (mu) goto not_found; +if (scan_keyword("em")) v=(@<The em width for |cur_font|@>); +@.em@> +else if (scan_keyword("ex")) v=(@<The x-height for |cur_font|@>); +@.ex@> +else goto not_found; +@<Scan an optional space@>; +found: cur_val=nx_plus_y(save_cur_val, v, xn_over_d(v, f, 0200000)); +goto attach_sign; +not_found: + +@ @<Scan for \(m)\.{mu} units and |goto attach_fraction|@>= +if (scan_keyword("mu")) goto attach_fraction; +@.mu@> +else{@+print_err("Illegal unit of measure (");print("mu inserted)"); +@.Illegal unit of measure@> + help4("The unit of measurement in math glue must be mu.",@/ + "To recover gracefully from this error, it's best to",@/ + "delete the erroneous units; e.g., type `2' to delete",@/ + "two letters. (See Chapter 27 of The TeXbook.)"); +@:TeXbook}{\sl The \TeX book@> + error();goto attach_fraction; + } + +@ @<Adjust \(f)for the magnification ratio@>= +{@+prepare_mag(); +if (mag!=1000) + {@+cur_val=xn_over_d(cur_val, 1000, mag); + f=(1000*f+0200000*rem)/mag; + cur_val=cur_val+(f/0200000);f=f%0200000; + } +} + +@ The necessary conversion factors can all be specified exactly as +fractions whose numerator and denominator sum to 32768 or less. +According to the definitions here, $\rm2660\,dd\approx1000.33297\,mm$; +this agrees well with the value $\rm1000.333\,mm$ cited by Bosshard +@^Bosshard, Hans Rudolf@> +in {\sl Technische Grundlagen zur Satzherstellung\/} (Bern, 1980). + +@d set_conversion(A, B) @+{@+num=A;denom=B;} + +@<Scan for \(a)all other units and adjust |cur_val| and |f|...@>= +if (scan_keyword("in")) set_conversion(7227, 100)@; +@.in@> +else if (scan_keyword("pc")) set_conversion(12, 1)@; +@.pc@> +else if (scan_keyword("cm")) set_conversion(7227, 254)@; +@.cm@> +else if (scan_keyword("mm")) set_conversion(7227, 2540)@; +@.mm@> +else if (scan_keyword("bp")) set_conversion(7227, 7200)@; +@.bp@> +else if (scan_keyword("dd")) set_conversion(1238, 1157)@; +@.dd@> +else if (scan_keyword("cc")) set_conversion(14856, 1157)@; +@.cc@> +else if (scan_keyword("sp")) goto done; +@.sp@> +else@<Complain about unknown unit and |goto done2|@>; +cur_val=xn_over_d(cur_val, num, denom); +f=(num*f+0200000*rem)/denom;@/ +cur_val=cur_val+(f/0200000);f=f%0200000; +done2: + +@ @<Complain about unknown unit...@>= +{@+print_err("Illegal unit of measure (");print("pt inserted)"); +@.Illegal unit of measure@> +help6("Dimensions can be in units of em, ex, in, pt, pc,",@/ + "cm, mm, dd, cc, bp, or sp; but yours is a new one!",@/ + "I'll assume that you meant to say pt, for printer's points.",@/ + "To recover gracefully from this error, it's best to",@/ + "delete the erroneous units; e.g., type `2' to delete",@/ + "two letters. (See Chapter 27 of The TeXbook.)"); +@:TeXbook}{\sl The \TeX book@> +error();goto done2; +} + + +@ @<Report that this dimension is out of range@>= +{@+print_err("Dimension too large"); +@.Dimension too large@> +help2("I can't work with sizes bigger than about 19 feet.",@/ + "Continue and I'll use the largest value I can.");@/ +error();cur_val=max_dimen;arith_error=false; +} + +@ The final member of \TeX's value-scanning trio is |scan_glue|, which +makes |cur_val| point to a glue specification. The reference count of that +glue spec will take account of the fact that |cur_val| is pointing to~it. + +The |level| parameter should be either |glue_val| or |mu_val|. + +Since |scan_dimen| was so much more complex than |scan_int|, we might expect +|scan_glue| to be even worse. But fortunately, it is very simple, since +most of the work has already been done. + +@p static void scan_glue(small_number @!level) + /*sets |cur_val| to a glue spec pointer*/ +{@+ +bool negative; /*should the answer be negated?*/ +pointer @!q; /*new glue specification*/ +bool @!mu; /*does |level==mu_val|?*/ +mu=(level==mu_val);@<Get the next non-blank non-sign...@>; +if ((cur_cmd >= min_internal)&&(cur_cmd <= max_internal)) + {@+scan_something_internal(level, negative); + if (cur_val_level >= glue_val) + {@+if (cur_val_level!=level) mu_error(); + return; + } + if (cur_val_level==int_val) scan_dimen(mu, false, true); + else if (level==mu_val) mu_error(); + } +else{@+back_input();scan_dimen(mu, false, false); + if (negative) negate(cur_val); + } +@<Create a new glue specification whose width is |cur_val|; scan for its stretch +and shrink components@>; +} +@# +@<Declare procedures needed for expressions@>@; + +@ @<Create a new glue specification whose width is |cur_val|...@>= +q=new_spec(zero_glue);width(q)=cur_val; +if (scan_keyword("plus")) +@.plus@> + {@+scan_dimen(mu, true, false); + stretch(q)=cur_val;stretch_order(q)=cur_order; + } +if (scan_keyword("minus")) +@.minus@> + {@+scan_dimen(mu, true, false); + shrink(q)=cur_val;shrink_order(q)=cur_order; + } +cur_val=q + +@ Here's a similar procedure that returns a pointer to a rule node. This +routine is called just after \TeX\ has seen \.{\\hrule} or \.{\\vrule}; +therefore |cur_cmd| will be either |hrule| or |vrule|. The idea is to store +the default rule dimensions in the node, then to override them if +`\.{height}' or `\.{width}' or `\.{depth}' specifications are +found (in any order). + +@d default_rule 26214 /*0.4\thinspace pt*/ + +@p static pointer scan_rule_spec(void) +{@+ +pointer q; /*the rule node being created*/ +q=new_rule(); /*|width|, |depth|, and |height| all equal |null_flag| now*/ +if (cur_cmd==vrule) width(q)=default_rule; +else{@+height(q)=default_rule;depth(q)=0; + } +reswitch: if (scan_keyword("width")) +@.width@> + {@+scan_normal_dimen;width(q)=cur_val;goto reswitch; + } +if (scan_keyword("height")) +@.height@> + {@+scan_normal_dimen;height(q)=cur_val;goto reswitch; + } +if (scan_keyword("depth")) +@.depth@> + {@+scan_normal_dimen;depth(q)=cur_val;goto reswitch; + } +return q; +} + +@* Building token lists. +The token lists for macros and for other things like \.{\\mark} and \.{\\output} +and \.{\\write} are produced by a procedure called |scan_toks|. + +Before we get into the details of |scan_toks|, let's consider a much +simpler task, that of converting the current string into a token list. +The |str_toks| function does this; it classifies spaces as type |spacer| +and everything else as type |other_char|. + +The token list created by |str_toks| begins at |link(temp_head)| and ends +at the value |p| that is returned. (If |p==temp_head|, the list is empty.) + +@p @t\4@>@<Declare \eTeX\ procedures for token lists@>@;@/ +static pointer str_toks(pool_pointer @!b) + /*converts |str_pool[b dotdot pool_ptr-1]| to a token list*/ +{@+pointer p; /*tail of the token list*/ +pointer @!q; /*new node being added to the token list via |store_new_token|*/ +halfword @!t; /*token being appended*/ +pool_pointer @!k; /*index into |str_pool|*/ +str_room(1); +p=temp_head;link(p)=null;k=b; +while (k < pool_ptr) + {@+t=so(str_pool[k]); + if (t==' ') t=space_token; + else t=other_token+t; + fast_store_new_token(t); + incr(k); + } +pool_ptr=b;return p; +} + +@ The main reason for wanting |str_toks| is the next function, +|the_toks|, which has similar input/output characteristics. + +This procedure is supposed to scan something like `\.{\\skip\\count12}', +i.e., whatever can follow `\.{\\the}', and it constructs a token list +containing something like `\.{-3.0pt minus 0.5fill}'. + +@p static pointer the_toks(void) +{@+ +int old_setting; /*holds |selector| setting*/ +pointer @!p, @!q, @!r; /*used for copying a token list*/ +pool_pointer @!b; /*base of temporary string*/ +small_number @!c; /*value of |cur_chr|*/ +@<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>;@/ +get_x_token();scan_something_internal(tok_val, false); +if (cur_val_level >= ident_val) @<Copy the token list@>@; +else{@+old_setting=selector;selector=new_string;b=pool_ptr; + switch (cur_val_level) { + case int_val: print_int(cur_val);@+break; + case dimen_val: {@+print_scaled(cur_val);print("pt"); + } @+break; + case glue_val: {@+print_spec(cur_val,"pt");delete_glue_ref(cur_val); + } @+break; + case mu_val: {@+print_spec(cur_val,"mu");delete_glue_ref(cur_val); + } + } /*there are no other cases*/ + selector=old_setting;return str_toks(b); + } +} + +@ @<Copy the token list@>= +{@+p=temp_head;link(p)=null; +if (cur_val_level==ident_val) store_new_token(cs_token_flag+cur_val)@; +else if (cur_val!=null) + {@+r=link(cur_val); /*do not copy the reference count*/ + while (r!=null) + {@+fast_store_new_token(info(r));r=link(r); + } + } +return p; +} + +@ Here's part of the |expand| subroutine that we are now ready to complete: + +@p static void ins_the_toks(void) +{@+link(garbage)=the_toks();ins_list(link(temp_head)); +} + +@ The primitives \.{\\number}, \.{\\romannumeral}, \.{\\string}, \.{\\meaning}, +\.{\\fontname}, and \.{\\jobname} are defined as follows. + +@d number_code 0 /*command code for \.{\\number}*/ +@d roman_numeral_code 1 /*command code for \.{\\romannumeral}*/ +@d string_code 2 /*command code for \.{\\string}*/ +@d meaning_code 3 /*command code for \.{\\meaning}*/ +@d font_name_code 4 /*command code for \.{\\fontname}*/ +@d etex_convert_base 5 /*base for \eTeX's command codes*/ +@d eTeX_revision_code etex_convert_base /*command code for \.{\\eTeXrevision}*/ +@d etex_convert_codes (etex_convert_base+1) /*end of \eTeX's command codes*/ +@d eTeX_last_convert_cmd_mod etex_convert_codes /* prote codes follow */ + + +@# +@d job_name_code pdftex_convert_codes /*command code for \.{\\jobname}*/ + +@<Put each...@>= +primitive("number", convert, number_code);@/ +@!@:number\_}{\.{\\number} primitive@> +primitive("romannumeral", convert, roman_numeral_code);@/ +@!@:roman\_numeral\_}{\.{\\romannumeral} primitive@> +primitive("string", convert, string_code);@/ +@!@:string\_}{\.{\\string} primitive@> +primitive("meaning", convert, meaning_code);@/ +@!@:meaning\_}{\.{\\meaning} primitive@> +primitive("fontname", convert, font_name_code);@/ +@!@:font\_name\_}{\.{\\fontname} primitive@> +primitive("jobname", convert, job_name_code);@/ +@!@:job\_name\_}{\.{\\jobname} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case convert: switch (chr_code) { + case number_code: print_esc("number");@+break; + case roman_numeral_code: print_esc("romannumeral");@+break; + case string_code: print_esc("string");@+break; + case meaning_code: print_esc("meaning");@+break; + case font_name_code: print_esc("fontname");@+break; + case job_name_code: print_esc("jobname");@+break; + case eTeX_revision_code: print_esc("eTeXrevision");@+break; + @/@<Cases of |convert| for |print_cmd_chr|@>@/ + } @+break; + +@ The procedure |conv_toks| uses |str_toks| to insert the token list +for |convert| functions into the scanner; `\.{\\outer}' control sequences +are allowed to follow `\.{\\string}' and `\.{\\meaning}'. + +@p static void scan_pdf_ext_toks(void); +static void conv_toks(void) +{@+int old_setting; /*holds |selector| setting*/ +int @!c; /*desired type of conversion*/ +small_number @!save_scanner_status; /*|scanner_status| upon entry*/ +pool_pointer @!b; /*base of temporary string*/ +int @!i, @!k, @!l; /*general purpose index*/ +pool_pointer @!m, @!n; /*general purpose pool pointer*/ +bool @!r; /*general purpose refraction i.e. changing the way*/ +str_number @!s, @!t; /*general purpose; de dicto*/ +c=cur_chr;@<Scan the argument for command |c|@>; +old_setting=selector;selector=new_string;b=pool_ptr; +@<Print the result of command |c|@>; +selector=old_setting;link(garbage)=str_toks(b);ins_list(link(temp_head)); +} + +@ @<Scan the argument for command |c|@>= +switch (c) { +case number_code: case roman_numeral_code: scan_int();@+break; +case string_code: case meaning_code: {@+save_scanner_status=scanner_status; + scanner_status=normal;get_token();scanner_status=save_scanner_status; + } @+break; +case font_name_code: scan_font_ident();@+break; +case job_name_code: if (job_name==0) open_log_file();@+break; +case eTeX_revision_code: do_nothing;@+break; +@/@<Cases of `Scan the argument for command |c|'@>@/ +} /*there are no other cases*/ + +@ @<Print the result of command |c|@>= +switch (c) { +case number_code: print_int(cur_val);@+break; +case roman_numeral_code: print_roman_int(cur_val);@+break; +case string_code: if (cur_cs!=0) sprint_cs(cur_cs); + else print_char(cur_chr);@+break; +case meaning_code: print_meaning();@+break; +case font_name_code: {@+printn(font_name[cur_val]); + if (font_size[cur_val]!=font_dsize[cur_val]) + {@+print(" at ");print_scaled(font_size[cur_val]); + print("pt"); + } + } @+break; +case eTeX_revision_code: print(eTeX_revision);@+break; +case job_name_code: printn(job_name);@+break; +@/@<Cases of `Print the result of command |c|'@>@/ +} /*there are no other cases*/ + +@ Now we can't postpone the difficulties any longer; we must bravely tackle +|scan_toks|. This function returns a pointer to the tail of a new token +list, and it also makes |def_ref| point to the reference count at the +head of that list. + +There are two boolean parameters, |macro_def| and |xpand|. If |macro_def| +is true, the goal is to create the token list for a macro definition; +otherwise the goal is to create the token list for some other \TeX\ +primitive: \.{\\mark}, \.{\\output}, \.{\\everypar}, \.{\\lowercase}, +\.{\\uppercase}, \.{\\message}, \.{\\errmessage}, \.{\\write}, or +\.{\\special}. In the latter cases a left brace must be scanned next; this +left brace will not be part of the token list, nor will the matching right +brace that comes at the end. If |xpand| is false, the token list will +simply be copied from the input using |get_token|. Otherwise all expandable +tokens will be expanded until unexpandable tokens are left, except that +the results of expanding `\.{\\the}' are not expanded further. +If both |macro_def| and |xpand| are true, the expansion applies +only to the macro body (i.e., to the material following the first +|left_brace| character). + +The value of |cur_cs| when |scan_toks| begins should be the |eqtb| +address of the control sequence to display in ``runaway'' error +messages. + +@p static pointer scan_toks(bool @!macro_def, bool @!xpand) +{@+ +halfword t; /*token representing the highest parameter number*/ +halfword @!s; /*saved token*/ +pointer @!p; /*tail of the token list being built*/ +pointer @!q; /*new node being added to the token list via |store_new_token|*/ +halfword @!unbalance; /*number of unmatched left braces*/ +halfword @!hash_brace; /*possible `\.{\#\{}' token*/ +if (macro_def) scanner_status=defining; +@+else scanner_status=absorbing; +warning_index=cur_cs;def_ref=get_avail();token_ref_count(def_ref)=null; +p=def_ref;hash_brace=0;t=zero_token; +if (macro_def) @<Scan and build the parameter part of the macro definition@>@; +else scan_left_brace(); /*remove the compulsory left brace*/ +@<Scan and build the body of the token list; |goto found| when finished@>; +found: scanner_status=normal; +if (hash_brace!=0) store_new_token(hash_brace); +return p; +} +@t\4@>@<Declare \Prote\ procedures for token lists@>@;@/ + +@ @<Scan and build the parameter part...@>= +{@+loop{@+resume: get_token(); /*set |cur_cmd|, |cur_chr|, |cur_tok|*/ + if (cur_tok < right_brace_limit) goto done1; + if (cur_cmd==mac_param) + @<If the next character is a parameter number, make |cur_tok| a |match| +token; but if it is a left brace, store `|left_brace|, |end_match|', set |hash_brace|, +and |goto done|@>; + store_new_token(cur_tok); + } +done1: store_new_token(end_match_token); +if (cur_cmd==right_brace) + @<Express shock at the missing left brace; |goto found|@>; +done: ;} + +@ @<Express shock...@>= +{@+print_err("Missing { inserted");incr(align_state); +@.Missing \{ inserted@> +help2("Where was the left brace? You said something like `\\def\\a}',",@/ + "which I'm going to interpret as `\\def\\a{}'.");error();goto found; +} + +@ @<If the next character is a parameter number...@>= +{@+s=match_token+cur_chr;get_token(); +if (cur_tok < left_brace_limit) + {@+hash_brace=cur_tok; + store_new_token(cur_tok);store_new_token(end_match_token); + goto done; + } +if (t==zero_token+9) + {@+print_err("You already have nine parameters"); +@.You already have nine...@> + help2("I'm going to ignore the # sign you just used,",@/ + "as well as the token that followed it.");error();goto resume; + } +else{@+incr(t); + if (cur_tok!=t) + {@+print_err("Parameters must be numbered consecutively"); +@.Parameters...consecutively@> + help2("I've inserted the digit you should have used after the #.",@/ + "Type `1' to delete what you did use.");back_error(); + } + cur_tok=s; + } +} + +@ @<Scan and build the body of the token list; |goto found| when finished@>= +unbalance=1; +loop@+{@+if (xpand) @<Expand the next part of the input@>@; + else get_token(); + if (cur_tok < right_brace_limit) + if (cur_cmd < right_brace) incr(unbalance); + else{@+decr(unbalance); + if (unbalance==0) goto found; + } + else if (cur_cmd==mac_param) + if (macro_def) @<Look for parameter number or \.{\#\#}@>; + store_new_token(cur_tok); + } + +@ Here we insert an entire token list created by |the_toks| without +expanding it further. + +@<Expand the next part of the input@>= +{@+loop{@+get_next(); + if (cur_cmd >= call) + if (info(link(cur_chr))==protected_token) + {@+cur_cmd=relax;cur_chr=no_expand_flag; + } + if (cur_cmd <= max_command) goto done2; + if (cur_cmd!=the) expand(); + else{@+q=the_toks(); + if (link(temp_head)!=null) + {@+link(p)=link(temp_head);p=q; + } + } + } +done2: x_token(); +} + +@ @<Look for parameter number...@>= +{@+s=cur_tok; +if (xpand) get_x_token();else get_token(); +if (cur_cmd!=mac_param) + if ((cur_tok <= zero_token)||(cur_tok > t)) + {@+print_err("Illegal parameter number in definition of "); +@.Illegal parameter number...@> + sprint_cs(warning_index); + help3("You meant to type ## instead of #, right?",@/ + "Or maybe a } was forgotten somewhere earlier, and things",@/ + "are all screwed up? I'm going to assume that you meant ##."); + back_error();cur_tok=s; + } + else cur_tok=out_param_token-'0'+cur_chr; +} + +@ Another way to create a token list is via the \.{\\read} command. The +sixteen files potentially usable for reading appear in the following +global variables. The value of |read_open[n]| will be |closed| if +stream number |n| has not been opened or if it has been fully read; +|just_open| if an \.{\\openin} but not a \.{\\read} has been done; +and |normal| if it is open and ready to read the next line. + +@d closed 2 /*not open, or at end of file*/ +@d just_open 1 /*newly opened, first line not yet read*/ + +@<Glob...@>= +static alpha_file @!read_file[16]; /*used for \.{\\read}*/ +static int8_t @!read_open[17]; /*state of |read_file[n]|*/ + +@ @<Set init...@>= +for (k=0; k<=16; k++) read_open[k]=closed; + +@ The |read_toks| procedure constructs a token list like that for any +macro definition, and makes |cur_val| point to it. Parameter |r| points +to the control sequence that will receive this token list. + +@p static void read_toks(int @!n, pointer @!r, halfword @!j) +{@+ +pointer p; /*tail of the token list*/ +pointer @!q; /*new node being added to the token list via |store_new_token|*/ +int @!s; /*saved value of |align_state|*/ +small_number @!m; /*stream number*/ +scanner_status=defining;warning_index=r; +def_ref=get_avail();token_ref_count(def_ref)=null; +p=def_ref; /*the reference count*/ +store_new_token(end_match_token); +if ((n < 0)||(n > 15)) m=16;@+else m=n; +s=align_state;align_state=1000000; /*disable tab marks, etc.*/ +@/do@+{@<Input and store tokens from the next line of the file@>; +}@+ while (!(align_state==1000000)); +cur_val=def_ref;scanner_status=normal;align_state=s; +} + +@ @<Input and store tokens from the next line of the file@>= +begin_file_reading();name=m+1; +if (read_open[m]==closed) @<Input for \.{\\read} from the terminal@>; +else if (read_open[m]==just_open) @<Input the first line of |read_file[m]|@>@; +else@<Input the next line of |read_file[m]|@>; +limit=last; +if (end_line_char_inactive) decr(limit); +else buffer[limit]=end_line_char; +first=limit+1;loc=start;state=new_line;@/ +@<Handle \.{\\readline} and |goto done|@>;@/ +loop@+{@+get_token(); + if (cur_tok==0) goto done; + /*|cur_cmd==cur_chr==0| will occur at the end of the line*/ + if (align_state < 1000000) /*unmatched `\.\}' aborts the line*/ + {@+@/do@+{get_token();}@+ while (!(cur_tok==0)); + align_state=1000000;goto done; + } + store_new_token(cur_tok); + } +done: end_file_reading() + +@ Here we input on-line into the |buffer| array, prompting the user explicitly +if |n >= 0|. The value of |n| is set negative so that additional prompts +will not be given in the case of multi-line input. + +@<Input for \.{\\read} from the terminal@>= +if (interaction > nonstop_mode) + if (n < 0) prompt_input("")@; + else{@+wake_up_terminal; + print_ln();sprint_cs(r);prompt_input("=");n=-1; + } +else fatal_error("*** (cannot \\read from terminal in nonstop modes)") +@.cannot \\read@> + +@ The first line of a file must be treated specially, since |input_ln| +must be told not to start with |get|. +@^system dependencies@> + +@<Input the first line of |read_file[m]|@>= +if (input_ln(&read_file[m], false)) read_open[m]=normal; +else{@+a_close(&read_file[m]);read_open[m]=closed; + } + +@ An empty line is appended at the end of a |read_file|. +@^empty line at end of file@> + +@<Input the next line of |read_file[m]|@>= +{@+if (!input_ln(&read_file[m], true)) + {@+a_close(&read_file[m]);read_open[m]=closed; + if (align_state!=1000000) + {@+runaway(); + print_err("File ended within ");print_esc("read"); +@.File ended within \\read@> + help1("This \\read has unbalanced braces."); + align_state=1000000;limit=0;error(); + } + } +} + +@* Conditional processing. +We consider now the way \TeX\ handles various kinds of \.{\\if} commands. + +@d unless_code 32 /*amount added for `\.{\\unless}' prefix*/ +@# +@d if_char_code 0 /* `\.{\\if}' */ +@d if_cat_code 1 /* `\.{\\ifcat}' */ +@d if_int_code 2 /* `\.{\\ifnum}' */ +@d if_dim_code 3 /* `\.{\\ifdim}' */ +@d if_odd_code 4 /* `\.{\\ifodd}' */ +@d if_vmode_code 5 /* `\.{\\ifvmode}' */ +@d if_hmode_code 6 /* `\.{\\ifhmode}' */ +@d if_mmode_code 7 /* `\.{\\ifmmode}' */ +@d if_inner_code 8 /* `\.{\\ifinner}' */ +@d if_void_code 9 /* `\.{\\ifvoid}' */ +@d if_hbox_code 10 /* `\.{\\ifhbox}' */ +@d if_vbox_code 11 /* `\.{\\ifvbox}' */ +@d ifx_code 12 /* `\.{\\ifx}' */ +@d if_eof_code 13 /* `\.{\\ifeof}' */ +@d if_true_code 14 /* `\.{\\iftrue}' */ +@d if_false_code 15 /* `\.{\\iffalse}' */ +@d if_case_code 16 /* `\.{\\ifcase}' */ + +@<Put each...@>= +primitive("if", if_test, if_char_code); +@!@:if\_char\_}{\.{\\if} primitive@> +primitive("ifcat", if_test, if_cat_code); +@!@:if\_cat\_code\_}{\.{\\ifcat} primitive@> +primitive("ifnum", if_test, if_int_code); +@!@:if\_int\_}{\.{\\ifnum} primitive@> +primitive("ifdim", if_test, if_dim_code); +@!@:if\_dim\_}{\.{\\ifdim} primitive@> +primitive("ifodd", if_test, if_odd_code); +@!@:if\_odd\_}{\.{\\ifodd} primitive@> +primitive("ifvmode", if_test, if_vmode_code); +@!@:if\_vmode\_}{\.{\\ifvmode} primitive@> +primitive("ifhmode", if_test, if_hmode_code); +@!@:if\_hmode\_}{\.{\\ifhmode} primitive@> +primitive("ifmmode", if_test, if_mmode_code); +@!@:if\_mmode\_}{\.{\\ifmmode} primitive@> +primitive("ifinner", if_test, if_inner_code); +@!@:if\_inner\_}{\.{\\ifinner} primitive@> +primitive("ifvoid", if_test, if_void_code); +@!@:if\_void\_}{\.{\\ifvoid} primitive@> +primitive("ifhbox", if_test, if_hbox_code); +@!@:if\_hbox\_}{\.{\\ifhbox} primitive@> +primitive("ifvbox", if_test, if_vbox_code); +@!@:if\_vbox\_}{\.{\\ifvbox} primitive@> +primitive("ifx", if_test, ifx_code); +@!@:ifx\_}{\.{\\ifx} primitive@> +primitive("ifeof", if_test, if_eof_code); +@!@:if\_eof\_}{\.{\\ifeof} primitive@> +primitive("iftrue", if_test, if_true_code); +@!@:if\_true\_}{\.{\\iftrue} primitive@> +primitive("iffalse", if_test, if_false_code); +@!@:if\_false\_}{\.{\\iffalse} primitive@> +primitive("ifcase", if_test, if_case_code); +@!@:if\_case\_}{\.{\\ifcase} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case if_test: {@+if (chr_code >= unless_code) print_esc("unless"); +switch (chr_code%unless_code) { + case if_cat_code: print_esc("ifcat");@+break; + case if_int_code: print_esc("ifnum");@+break; + case if_dim_code: print_esc("ifdim");@+break; + case if_odd_code: print_esc("ifodd");@+break; + case if_vmode_code: print_esc("ifvmode");@+break; + case if_hmode_code: print_esc("ifhmode");@+break; + case if_mmode_code: print_esc("ifmmode");@+break; + case if_inner_code: print_esc("ifinner");@+break; + case if_void_code: print_esc("ifvoid");@+break; + case if_hbox_code: print_esc("ifhbox");@+break; + case if_vbox_code: print_esc("ifvbox");@+break; + case ifx_code: print_esc("ifx");@+break; + case if_eof_code: print_esc("ifeof");@+break; + case if_true_code: print_esc("iftrue");@+break; + case if_false_code: print_esc("iffalse");@+break; + case if_case_code: print_esc("ifcase");@+break; + @/@<Cases of |if_test| for |print_cmd_chr|@>@/ + default:print_esc("if"); + } +} @+break; + +@ Conditions can be inside conditions, and this nesting has a stack +that is independent of the |save_stack|. + +Four global variables represent the top of the condition stack: +|cond_ptr| points to pushed-down entries, if any; |if_limit| specifies +the largest code of a |fi_or_else| command that is syntactically legal; +|cur_if| is the name of the current type of conditional; and |if_line| +is the line number at which it began. + +If no conditions are currently in progress, the condition stack has the +special state |cond_ptr==null|, |if_limit==normal|, |cur_if==0|, |if_line==0|. +Otherwise |cond_ptr| points to a two-word node; the |type|, |subtype|, and +|link| fields of the first word contain |if_limit|, |cur_if|, and +|cond_ptr| at the next level, and the second word contains the +corresponding |if_line|. + +@d if_node_size 2 /*number of words in stack entry for conditionals*/ +@d if_line_field(A) mem[A+1].i +@d if_code 1 /*code for \.{\\if...} being evaluated*/ +@d fi_code 2 /*code for \.{\\fi}*/ +@d else_code 3 /*code for \.{\\else}*/ +@d or_code 4 /*code for \.{\\or}*/ + +@<Glob...@>= +static pointer @!cond_ptr; /*top of the condition stack*/ +static int @!if_limit; /*upper bound on |fi_or_else| codes*/ +static small_number @!cur_if; /*type of conditional being worked on*/ +static int @!if_line; /*line where that conditional began*/ + +@ @<Set init...@>= +cond_ptr=null;if_limit=normal;cur_if=0;if_line=0; + +@ @<Put each...@>= +primitive("fi", fi_or_else, fi_code); +@!@:fi\_}{\.{\\fi} primitive@> +text(frozen_fi)=text(cur_val);eqtb[frozen_fi]=eqtb[cur_val]; +primitive("or", fi_or_else, or_code); +@!@:or\_}{\.{\\or} primitive@> +primitive("else", fi_or_else, else_code); +@!@:else\_}{\.{\\else} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case fi_or_else: if (chr_code==fi_code) print_esc("fi"); + else if (chr_code==or_code) print_esc("or"); + else print_esc("else");@+break; + +@ When we skip conditional text, we keep track of the line number +where skipping began, for use in error messages. + +@<Glob...@>= +static int @!skip_line; /*skipping began here*/ + +@ Here is a procedure that ignores text until coming to an \.{\\or}, +\.{\\else}, or \.{\\fi} at the current level of $\.{\\if}\ldots\.{\\fi}$ +nesting. After it has acted, |cur_chr| will indicate the token that +was found, but |cur_tok| will not be set (because this makes the +procedure run faster). + +@p static void pass_text(void) +{@+ +int l; /*level of $\.{\\if}\ldots\.{\\fi}$ nesting*/ +small_number @!save_scanner_status; /*|scanner_status| upon entry*/ +save_scanner_status=scanner_status;scanner_status=skipping;l=0; +skip_line=line; +loop@+{@+get_next(); + if (cur_cmd==fi_or_else) + {@+if (l==0) goto done; + if (cur_chr==fi_code) decr(l); + } + else if (cur_cmd==if_test) incr(l); + } +done: scanner_status=save_scanner_status; +if (tracing_ifs > 0) show_cur_cmd_chr(); +} + +@ When we begin to process a new \.{\\if}, we set |if_limit=if_code|; then +if\/ \.{\\or} or \.{\\else} or \.{\\fi} occurs before the current \.{\\if} +condition has been evaluated, \.{\\relax} will be inserted. +For example, a sequence of commands like `\.{\\ifvoid1\\else...\\fi}' +would otherwise require something after the `\.1'. + +@<Push the condition stack@>= +{@+p=get_node(if_node_size);link(p)=cond_ptr;type(p)=if_limit; +subtype(p)=cur_if;if_line_field(p)=if_line; +cond_ptr=p;cur_if=cur_chr;if_limit=if_code;if_line=line; +} + +@ @<Pop the condition stack@>= +{@+if (if_stack[in_open]==cond_ptr) if_warning(); + /*conditionals possibly not properly nested with files*/ +p=cond_ptr;if_line=if_line_field(p); +cur_if=subtype(p);if_limit=type(p);cond_ptr=link(p); +free_node(p, if_node_size); +} + +@ Here's a procedure that changes the |if_limit| code corresponding to +a given value of |cond_ptr|. + +@p static void change_if_limit(small_number @!l, pointer @!p) +{@+ +pointer q; +if (p==cond_ptr) if_limit=l; /*that's the easy case*/ +else{@+q=cond_ptr; + loop@+{@+if (q==null) confusion("if"); +@:this can't happen if}{\quad if@> + if (link(q)==p) + {@+type(q)=l;return; + } + q=link(q); + } + } +} + +@ A condition is started when the |expand| procedure encounters +an |if_test| command; in that case |expand| reduces to |conditional|, +which is a recursive procedure. +@^recursion@> + +@p static void conditional(void) +{@+ +bool b; /*is the condition true?*/ +int @!r; /*relation to be evaluated*/ +int @!m, @!n; /*to be tested against the second operand*/ +pointer @!p, @!q; /*for traversing token lists in \.{\\ifx} tests*/ +small_number @!save_scanner_status; /*|scanner_status| upon entry*/ +pointer @!save_cond_ptr; /*|cond_ptr| corresponding to this conditional*/ +small_number @!this_if; /*type of this conditional*/ +bool @!is_unless; /*was this if preceded by `\.{\\unless}' ?*/ +if (tracing_ifs > 0) if (tracing_commands <= 1) show_cur_cmd_chr(); +@<Push the condition stack@>;@+save_cond_ptr=cond_ptr; +is_unless=(cur_chr >= unless_code);this_if=cur_chr%unless_code;@/ +@<Either process \.{\\ifcase} or set |b| to the value of a boolean condition@>; +if (is_unless) b=!b; +if (tracing_commands > 1) @<Display the value of |b|@>; +if (b) + {@+change_if_limit(else_code, save_cond_ptr); + return; /*wait for \.{\\else} or \.{\\fi}*/ + } +@<Skip to \.{\\else} or \.{\\fi}, then |goto common_ending|@>; +common_ending: if (cur_chr==fi_code) @<Pop the condition stack@>@; +else if_limit=fi_code; /*wait for \.{\\fi}*/ +} + +@ In a construction like `\.{\\if\\iftrue abc\\else d\\fi}', the first +\.{\\else} that we come to after learning that the \.{\\if} is false is +not the \.{\\else} we're looking for. Hence the following curious +logic is needed. + +@ @<Skip to \.{\\else} or \.{\\fi}...@>= +loop@+{@+pass_text(); + if (cond_ptr==save_cond_ptr) + {@+if (cur_chr!=or_code) goto common_ending; + print_err("Extra ");print_esc("or"); +@.Extra \\or@> + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } + else if (cur_chr==fi_code) @<Pop the condition stack@>; + } + +@ @<Either process \.{\\ifcase} or set |b|...@>= +switch (this_if) { +case if_char_code: case if_cat_code: @<Test if two characters match@>@;@+break; +case if_int_code: case if_dim_code: @<Test relation between integers or dimensions@>@;@+break; +case if_odd_code: @<Test if an integer is odd@>@;@+break; +case if_vmode_code: b=(abs(mode)==vmode);@+break; +case if_hmode_code: b=(abs(mode)==hmode);@+break; +case if_mmode_code: b=(abs(mode)==mmode);@+break; +case if_inner_code: b=(mode < 0);@+break; +case if_void_code: case if_hbox_code: + case if_vbox_code: @<Test box register status@>@;@+break; +case ifx_code: @<Test if two tokens match@>@;@+break; +case if_eof_code: {@+scan_four_bit_int();b=(read_open[cur_val]==closed); + } @+break; +case if_true_code: b=true;@+break; +case if_false_code: b=false;@+break; +@/@<Cases for |conditional|@>@/ +case if_case_code: @<Select the appropriate case and |return| or |goto common_ending|@>; +} /*there are no other cases*/ + +@ @<Display the value of |b|@>= +{@+begin_diagnostic(); +if (b) print("{true}");@+else print("{false}"); +end_diagnostic(false); +} + +@ Here we use the fact that |'<'|, |'='|, and |'>'| are consecutive ASCII +codes. +@^ASCII code@> + +@<Test relation between integers or dimensions@>= +{@+if (this_if==if_int_code) scan_int();@+else scan_normal_dimen; +n=cur_val;@<Get the next non-blank non-call...@>; +if ((cur_tok >= other_token+'<')&&(cur_tok <= other_token+'>')) + r=cur_tok-other_token; +else{@+print_err("Missing = inserted for "); +@.Missing = inserted@> + print_cmd_chr(if_test, this_if); + help1("I was expecting to see `<', `=', or `>'. Didn't."); + back_error();r='='; + } +if (this_if==if_int_code) scan_int();@+else scan_normal_dimen; +switch (r) { +case '<': b=(n < cur_val);@+break; +case '=': b=(n==cur_val);@+break; +case '>': b=(n > cur_val); +} +} + +@ @<Test if an integer is odd@>= +{@+scan_int();b=odd(cur_val); +} + +@ @<Test box register status@>= +{@+scan_register_num();fetch_box(p); +if (this_if==if_void_code) b=(p==null); +else if (p==null) b=false; +else if (this_if==if_hbox_code) b=(type(p)==hlist_node); +else b=(type(p)==vlist_node); +} + +@ An active character will be treated as category 13 following +\.{\\if\\noexpand} or following \.{\\ifcat\\noexpand}. We use the fact that +active characters have the smallest tokens, among all control sequences. + +@d get_x_token_or_active_char @t@>@; + {@+get_x_token(); + if (cur_cmd==relax) if (cur_chr==no_expand_flag) + {@+cur_cmd=active_char; + cur_chr=cur_tok-cs_token_flag-active_base; + } + } + +@<Test if two characters match@>= +{@+get_x_token_or_active_char; +if ((cur_cmd > active_char)||(cur_chr > 255)) /*not a character*/ + {@+m=relax;n=256; + } +else{@+m=cur_cmd;n=cur_chr; + } +get_x_token_or_active_char; +if ((cur_cmd > active_char)||(cur_chr > 255)) + {@+cur_cmd=relax;cur_chr=256; + } +if (this_if==if_char_code) b=(n==cur_chr);@+else b=(m==cur_cmd); +} + +@ Note that `\.{\\ifx}' will declare two macros different if one is \\{long} +or \\{outer} and the other isn't, even though the texts of the macros are +the same. + +We need to reset |scanner_status|, since \.{\\outer} control sequences +are allowed, but we might be scanning a macro definition or preamble. + +@<Test if two tokens match@>= +{@+save_scanner_status=scanner_status;scanner_status=normal; +get_next();n=cur_cs;p=cur_cmd;q=cur_chr; +get_next();if (cur_cmd!=p) b=false; +else if (cur_cmd < call) b=(cur_chr==q); +else@<Test if two macro texts match@>; +scanner_status=save_scanner_status; +} + +@ Note also that `\.{\\ifx}' decides that macros \.{\\a} and \.{\\b} are +different in examples like this: +$$\vbox{\halign{\.{#}\hfil&\qquad\.{#}\hfil\cr + {}\\def\\a\{\\c\}& + {}\\def\\c\{\}\cr + {}\\def\\b\{\\d\}& + {}\\def\\d\{\}\cr}}$$ + +@<Test if two macro texts match@>= +{@+p=link(cur_chr);q=link(equiv(n)); /*omit reference counts*/ +if (p==q) b=true; +else{@+while ((p!=null)&&(q!=null)) + if (info(p)!=info(q)) p=null; + else{@+p=link(p);q=link(q); + } + b=((p==null)&&(q==null)); + } +} + +@ @<Select the appropriate case and |return| or |goto common_ending|@>= +{@+scan_int();n=cur_val; /*|n| is the number of cases to pass*/ +if (tracing_commands > 1) + {@+begin_diagnostic();print("{case ");print_int(n);print_char('}'); + end_diagnostic(false); + } +while (n!=0) + {@+pass_text(); + if (cond_ptr==save_cond_ptr) + if (cur_chr==or_code) decr(n); + else goto common_ending; + else if (cur_chr==fi_code) @<Pop the condition stack@>; + } +change_if_limit(or_code, save_cond_ptr); +return; /*wait for \.{\\or}, \.{\\else}, or \.{\\fi}*/ +} + +@ The processing of conditionals is complete except for the following +code, which is actually part of |expand|. It comes into play when +\.{\\or}, \.{\\else}, or \.{\\fi} is scanned. + +@<Terminate the current conditional and skip to \.{\\fi}@>= +{@+if (tracing_ifs > 0) if (tracing_commands <= 1) show_cur_cmd_chr(); +if (cur_chr > if_limit) + if (if_limit==if_code) insert_relax(); /*condition not yet evaluated*/ + else{@+print_err("Extra ");print_cmd_chr(fi_or_else, cur_chr); +@.Extra \\or@> +@.Extra \\else@> +@.Extra \\fi@> + help1("I'm ignoring this; it doesn't match any \\if."); + error(); + } +else{@+while (cur_chr!=fi_code) pass_text(); /*skip to \.{\\fi}*/ + @<Pop the condition stack@>; + } +} + +@* File names. +It's time now to fret about file names. Besides the fact that different +operating systems treat files in different ways, we must cope with the +fact that completely different naming conventions are used by different +groups of people. The following programs show what is required for one +particular operating system; similar routines for other systems are not +difficult to devise. +@^fingers@> +@^system dependencies@> + +\TeX\ assumes that a file name has three parts: the name proper; its +``extension''; and a ``file area'' where it is found in an external file +system. The extension of an input file or a write file is assumed to be +`\.{.tex}' unless otherwise specified; it is `\.{.log}' on the +transcript file that records each run of \TeX; it is `\.{.tfm}' on the font +metric files that describe characters in the fonts \TeX\ uses; it is +`\.{.dvi}' on the output files that specify typesetting information; and it +is `\.{.fmt}' on the format files written by \.{INITEX} to initialize \TeX. +The file area can be arbitrary on input files, but files are usually +output to the user's current area. If an input file cannot be +found on the specified area, \TeX\ will look for it on a special system +area; this special area is intended for commonly used input files like +\.{webmac.tex}. + +Simple uses of \TeX\ refer only to file names that have no explicit +extension or area. For example, a person usually says `\.{\\input} \.{paper}' +or `\.{\\font\\tenrm} \.= \.{helvetica}' instead of `\.{\\input} +\.{paper.new}' or `\.{\\font\\tenrm} \.= \.{<csd.knuth>test}'. Simple file +names are best, because they make the \TeX\ source files portable; +whenever a file name consists entirely of letters and digits, it should be +treated in the same way by all implementations of \TeX. However, users +need the ability to refer to other files in their environment, especially +when responding to error messages concerning unopenable files; therefore +we want to let them use the syntax that appears in their favorite +operating system. + +The following procedures don't allow spaces to be part of +file names; but some users seem to like names that are spaced-out. +System-dependent changes to allow such things should probably +be made with reluctance, and only when an entire file name that +includes spaces is ``quoted'' somehow. + +@ In order to isolate the system-dependent aspects of file names, the +@^system dependencies@> +system-independent parts of \TeX\ are expressed in terms +of three system-dependent +procedures called |begin_name|, |more_name|, and |end_name|. In +essence, if the user-specified characters of the file name are $c_1\ldots c_n$, +the system-independent driver program does the operations +$$|begin_name|;\,|more_name|(c_1);\,\ldots\,;\,|more_name|(c_n); +\,|end_name|.$$ +These three procedures communicate with each other via global variables. +Afterwards the file name will appear in the string pool as three strings +called |cur_name|\penalty10000\hskip-.05em, +|cur_area|, and |cur_ext|; the latter two are null (i.e., +|""|), unless they were explicitly specified by the user. + +Actually the situation is slightly more complicated, because \TeX\ needs +to know when the file name ends. The |more_name| routine is a function +(with side effects) that returns |true| on the calls |more_name|$(c_1)$, +\dots, |more_name|$(c_{n-1})$. The final call |more_name|$(c_n)$ +returns |false|; or, it returns |true| and the token following $c_n$ is +something like `\.{\\hbox}' (i.e., not a character). In other words, +|more_name| is supposed to return |true| unless it is sure that the +file name has been completely scanned; and |end_name| is supposed to be able +to finish the assembly of |cur_name|, |cur_area|, and |cur_ext| regardless of +whether $|more_name|(c_n)$ returned |true| or |false|. + +@<Glob...@>= +static str_number @!cur_name; /*name of file just scanned*/ +static str_number @!cur_area; /*file area just scanned, or \.{""}*/ +static str_number @!cur_ext; /*file extension just scanned, or \.{""}*/ + +@ The file names we shall deal with for illustrative purposes have the +following structure: If the name contains `\.>' or `\.:', the file area +consists of all characters up to and including the final such character; +otherwise the file area is null. If the remaining file name contains +`\..', the file extension consists of all such characters from the first +remaining `\..' to the end, otherwise the file extension is null. +@^system dependencies@> + +We can scan such file names easily by using two global variables that keep track +of the occurrences of area and extension delimiters: + +@<Glob...@>= +static pool_pointer @!area_delimiter; /*the most recent `\.>' or `\.:', if any*/ +static pool_pointer @!ext_delimiter; /*the relevant `\..', if any*/ + +@ Input files that can't be found in the user's area may appear in a standard +system area called |TEX_area|. Font metric files whose areas are not given +explicitly are assumed to appear in a standard system area called +|TEX_font_area|. These system area names will, of course, vary from place +to place. +@^system dependencies@> + +@d TEX_area "TeXinputs/" +@.TeXinputs@> +@d TEX_font_area "TeXfonts/" +@.TeXfonts@> + +@ Here now is the first of the system-dependent routines for file name scanning. +@^system dependencies@> + +@p static bool quoted_filename; +static void begin_name(void) +{@+area_delimiter=0;ext_delimiter=0; quoted_filename=false; +} + +@ And here's the second. The string pool might change as the file name is +being scanned, since a new \.{\\csname} might be entered; therefore we keep +|area_delimiter| and |ext_delimiter| relative to the beginning of the current +string, instead of assigning an absolute address like |pool_ptr| to them. +@^system dependencies@> + +@p static bool more_name(ASCII_code @!c) +{@+if (c==' ' && !quoted_filename) return false; +else if (c=='"') {@+quoted_filename=!quoted_filename; return true; } +else {@+str_room(1);append_char(c); /*contribute |c| to the current string*/ + if (IS_DIR_SEP(c)) {@+area_delimiter=cur_length;ext_delimiter=0; } + else if (c=='.') ext_delimiter=cur_length; + return true; + } +} + +@ The third. +@^system dependencies@> + +@p static void end_name(void) +{@+if (str_ptr+3 > max_strings) + overflow("number of strings", max_strings-init_str_ptr); +@:TeX capacity exceeded number of strings}{\quad number of strings@> +if (area_delimiter==0) cur_area=empty_string; +else{@+cur_area=str_ptr; + str_start[str_ptr+1]=str_start[str_ptr]+area_delimiter;incr(str_ptr); + } +if (ext_delimiter==0) + {@+cur_ext=empty_string;cur_name=make_string(); + } +else{@+cur_name=str_ptr; + str_start[str_ptr+1]=str_start[str_ptr]+ext_delimiter-area_delimiter-1; + incr(str_ptr);cur_ext=make_string(); + } +} + +@ 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...@>= +static void print_file_name(int @!n, int @!a, int @!e) +{@+slow_print(a);slow_print(n);slow_print(e); +} + +@ Another system-dependent routine is needed to convert three internal +\TeX\ strings +into the |name_of_file| value that is used to open files. The present code +allows both lowercase and uppercase letters in the file name. +@^system dependencies@> + +@d append_to_name(A) {@+c=A;incr(k); + if (k <= file_name_size) name_of_file[k]=xchr[c]; + } + +@p static void pack_file_name(str_number @!n, str_number @!a, str_number @!e, char *@!f) +{@+int k; /*number of positions filled in |name_of_file|*/ +ASCII_code @!c; /*character being packed*/ +int @!j; /*index into |str_pool|*/ +k=0; +for (j=str_start[a]; j<=str_start[a+1]-1; j++) append_to_name(so(str_pool[j]))@; +for (j=str_start[n]; j<=str_start[n+1]-1; j++) append_to_name(so(str_pool[j]))@; +if (f==NULL) + for (j=str_start[e]; j<=str_start[e+1]-1; j++) append_to_name(so(str_pool[j]))@; +else + while(*f!=0) append_to_name(so(*f++))@; +if (k <= file_name_size) name_length=k;@+else name_length=file_name_size; +name_of_file[name_length+1]=0; +} + + + +@ \TeX\ Live does not use the global variable |TEX_format_default|. It is no longer needed +to supply the text for default system areas +and extensions related to format files. + +@ Consequently \TeX\ Live does not need the initialization of |TEX_format_default| either. + +@ And \TeX\ Live does not check the length of |TEX_format_default|. + +@ The |format_extension|, however, is needed by \TeX\ Live +to create the format name from the job name. + +@d format_extension ".fmt" + +@ This part of the program +becomes active when a ``virgin'' \TeX\ is trying to get going, just after +the preliminary initialization, or when the user is substituting another +format file by typing `\.\&' after the initial `\.{**}' prompt. The buffer +contains the first line of input in |buffer[loc dotdot(last-1)]|, where +|loc < last| and |buffer[loc]!=' '|. + +\TeX\ Live uses the {\tt kpathsearch} library to implement access to files. +|open_fmt_file| is declared here and the +actual implementation is in the section on \TeX\ Live Integration. + +@<Declare the function called |open_fmt_file|@>= +static bool open_fmt_file(void); + +@ Operating systems often make it possible to determine the exact name (and +possible version number) of a file that has been opened. The following routine, +which simply makes a \TeX\ string from the value of |name_of_file|, should +ideally be changed to deduce the full name of file~|f|, which is the file +most recently opened, if it is possible to do this in a \PASCAL\ program. +@^system dependencies@> + +This routine might be called after string memory has overflowed, hence +we dare not use `|str_room|'. + +@p static str_number make_name_string(void) +{@+int k; /*index into |name_of_file|*/ +if ((pool_ptr+name_length > pool_size)||(str_ptr==max_strings)|| + (cur_length > 0)) + return'?'; +else{@+for (k=1; k<=name_length; k++) append_char(xord[name_of_file[k]]); + return make_string(); + } +} +static str_number a_make_name_string(alpha_file *f) +{@+return make_name_string(); +} +static str_number b_make_name_string(byte_file *f) +{@+return make_name_string(); +} +#ifdef @!INIT +static str_number w_make_name_string(word_file *f) +{@+return make_name_string(); +} +#endif + +@ Now let's consider the ``driver'' +routines by which \TeX\ deals with file names +in a system-independent manner. First comes a procedure that looks for a +file name. +There are two ways to specify the file name: as a general text argument +or as a token (after expansion). +The traditional token delimiter is the space. For a file name, however, +a double quote is used as the token delimiter if the token starts +with a double quote. + +Once the |area_delimiter| and the |ext_delimiter| are defined, +the final processing is shared for all variants. + +When starting, \.{\\relax} is skipped as well as blanks and non-calls. +Then a test for the |left_brace| will branch to the code for scanning +a general text. +% consider the case where a double quote is made an active character +% equivalent to a left brace! + +@p static void scan_file_name(void) +{@+ +pool_pointer @!j, k; /*index into |str_pool|*/ +int @!old_setting; /*holds |selector| setting*/ +name_in_progress=true;begin_name(); +@<Get the next non-blank non-relax...@>; +if (cur_cmd==left_brace) + @<Define a general text file name and |goto done|@>@; +loop@+{@+if ((cur_cmd > other_char)||(cur_chr > 255)) /*not a character*/ + {@+back_input();goto done; + } +#if 0 + /* This is from pdftex-final.ch. I don't know these `some cases', + and I am not sure whether the name should end even if quoting is on.*/ + /*If |cur_chr| is a space and we're not scanning a token list, check + whether we're at the end of the buffer. Otherwise we end up adding + spurious spaces to file names in some cases.*/ + if (cur_chr==' ' && state!=token_list && loc>limit) goto done; +#endif + if (!more_name(cur_chr)) goto done; + get_x_token(); + } +done: end_name();name_in_progress=false; +} + +@ The global variable |name_in_progress| is used to prevent recursive +use of |scan_file_name|, since the |begin_name| and other procedures +communicate via global variables. Recursion would arise only by +devious tricks like `\.{\\input\\input f}'; such attempts at sabotage +must be thwarted. Furthermore, |name_in_progress| prevents \.{\\input} +@^recursion@> +from being initiated when a font size specification is being scanned. + +Another global variable, |job_name|, contains the file name that was first +\.{\\input} by the user. This name is extended by `\.{.log}' and `\.{.dvi}' +and `\.{.fmt}' in the names of \TeX's output files. + +@<Glob...@>= +static bool @!name_in_progress; /*is a file name being scanned?*/ +static str_number @!job_name; /*principal file name*/ +static bool @!log_opened; /*has the transcript file been opened?*/ + +@ Initially |job_name==0|; it becomes nonzero as soon as the true name is known. +We have |job_name==0| if and only if the `\.{log}' file has not been opened, +except of course for a short time just after |job_name| has become nonzero. + +@<Initialize the output...@>= +job_name=0;name_in_progress=false;log_opened=false; + +@ Here is a routine that manufactures the output file names, assuming that +|job_name!=0|. It ignores and changes the current settings of |cur_area| +and |cur_ext|. + +@d pack_cur_name(A) if (cur_ext==empty_string) pack_file_name(cur_name, cur_area, cur_ext,A); + else pack_file_name(cur_name, cur_area, cur_ext,NULL) + +@p static void pack_job_name(char *@!s) /*|s==".log"|, |".dvi"|, or + |format_extension|*/ +{@+cur_area=empty_string;cur_ext=empty_string; +cur_name=job_name;pack_cur_name(s); +} + +@ If some trouble arises when \TeX\ tries to open a file, the following +routine calls upon the user to supply another file name. Parameter~|s| +is used in the error message to identify the type of file; parameter~|e| +is the default extension if none is given. +We handle the specification of a file name with possibly spaces in +double quotes (the last one is optional if this is the end of line +i.e. the end of the buffer). +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 static void prompt_file_name(char *@!s, char *@!e) +{@+ +int k; /*index into |buffer|*/ +if (interaction==scroll_mode) wake_up_terminal; +if (strcmp(s,"input file name")==0) 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 (strcmp(e,".tex")==0) show_context(); +print_nl("Please type another ");print(s); +@.Please type...@> +if (interaction < scroll_mode) + fatal_error("*** (job aborted, file error in nonstop mode)"); +@.job aborted, file error...@> +clear_terminal;prompt_input(": ");@<Scan file name in the buffer@>; +pack_cur_name(e); +} + +@ @<Scan file name in the buffer@>= +{@+ +begin_name();k=first; +while ((buffer[k]==' ')&&(k < last)) incr(k); +loop@+{@+if (k==last) goto done; + if (!more_name(buffer[k])) goto done; + incr(k); + } +done: end_name(); +} + +@ Here's an example of how these conventions are used. Whenever it is time to +ship out a box of stuff, we shall use the macro |ensure_dvi_open|. + +@d ensure_dvi_open if (output_file_name==0) + {@+if (job_name==0) open_log_file(); + pack_job_name(".dvi"); + while (!b_open_out(&dvi_file)) + prompt_file_name("file name for output",".dvi"); + output_file_name=b_make_name_string(&dvi_file); + } + +@<Glob...@>= +static byte_file @!dvi_file; /*the device-independent output goes here*/ +static str_number @!output_file_name; /*full name of the output file*/ +static str_number @!log_name; /*full name of the log file*/ + +@ @<Initialize the output...@>=output_file_name=0; + +@ The |open_log_file| routine is used to open the transcript file and to help +it catch up to what has previously been printed on the terminal. + +@p static void open_log_file(void) +{@+int old_setting; /*previous |selector| setting*/ +int @!k; /*index into |months| and |buffer|*/ +int @!l; /*end of first input line*/ +char @!months[]=" JANFEBMARAPRMAYJUNJULAUGSEPOCTNOVDEC"; /*abbreviations of month names*/ +old_setting=selector; +if (job_name==0) job_name=s_no(c_job_name?c_job_name:"texput"); /* \TeX\ Live */ +pack_job_name(".fls"); +recorder_change_filename((char *)name_of_file+1); +@.texput@> +pack_job_name(".log"); +while (!a_open_out(&log_file)) @<Try to get a different log file name@>; +log_name=a_make_name_string(&log_file); +selector=log_only;log_opened=true; +@<Print the banner line, including the date and time@>; +input_stack[input_ptr]=cur_input; /*make sure bottom level is in memory*/ +print_nl("**"); +@.**@> +l=input_stack[0].limit_field; /*last position of first line*/ +if (buffer[l]==end_line_char) decr(l); +for (k=1; k<=l; k++) printn(buffer[k]); +print_ln(); /*now the transcript file contains the first line of input*/ +selector=old_setting+2; /*|log_only| or |term_and_log|*/ +} + +@ Sometimes |open_log_file| is called at awkward moments when \TeX\ is +unable to print error messages or even to |show_context|. +The |prompt_file_name| routine can result in a |fatal_error|, but the |error| +routine will not be invoked because |log_opened| will be false. + +The normal idea of |batch_mode| is that nothing at all should be written +on the terminal. However, in the unusual case that +no log file could be opened, we make an exception and allow +an explanatory message to be seen. + +Incidentally, the program always refers to the log file as a `\.{transcript +file}', because some systems cannot use the extension `\.{.log}' for +this file. + +@<Try to get a different log file name@>= +{@+selector=term_only; +prompt_file_name("transcript file name",".log"); +} + +@ @<Print the banner...@>= +{@+wlog("%s",banner); +slow_print(format_ident);print(" "); +print_int(sys_day);print_char(' '); +for (k=3*sys_month-2; k<=3*sys_month; k++) wlog("%c",months[k]); +print_char(' ');print_int(sys_year);print_char(' '); +print_two(sys_time/60);print_char(':');print_two(sys_time%60); +if (eTeX_ex) + {@+;wlog_cr;wlog("entering extended mode"); + } +if (Prote_ex) + {@+;wlog_cr;wlog("entering Prote mode"); + } +} + +@ Let's turn now to the procedure that is used to initiate file reading +when an `\.{\\input}' command is being processed. +Beware: For historic reasons, this code foolishly conserves a tiny bit +of string pool space; but that can confuse the interactive `\.E' option. +@^system dependencies@> + +@p static void start_input(void) /*\TeX\ will \.{\\input} something*/ +{@+ +scan_file_name(); /*set |cur_name| to desired file name*/ +pack_cur_name(""); +loop@+{@+begin_file_reading(); /*set up |cur_file| and new level of input*/ + if (kpse_in_name_ok((char*)name_of_file+1) && a_open_in(&cur_file)) goto done; + end_file_reading(); /*remove the level that didn't work*/ + prompt_file_name("input file name",".tex"); + } +done: name=a_make_name_string(&cur_file);@/ +if (source_filename_stack[in_open]!=NULL) + free(source_filename_stack[in_open]); +source_filename_stack[in_open]=strdup((char *)name_of_file+1); /*\TeX\ Live*/ +if (full_source_filename_stack[in_open]!=NULL) + free(full_source_filename_stack[in_open]); +full_source_filename_stack[in_open]=strdup(full_name_of_file); +@<Set new |cur_file_num|@>@; +/* new entry on the macro stack */ +{ @<additional local variables for |start_input|@> + @<update the macro stack@>@; +} +if (job_name==0) + {@+if (c_job_name==NULL) job_name=cur_name; + else job_name=s_no(c_job_name); open_log_file(); /* \TeX\ Live*/ + } /*|open_log_file| doesn't |show_context|, so |limit| + and |loc| needn't be set to meaningful values yet*/ +if (term_offset+strlen(full_source_filename_stack[in_open]) > max_print_line-2) + print_ln(); +else if ((term_offset > 0)||(file_offset > 0)) print_char(' '); +print_char('(');incr(open_parens); +print(full_source_filename_stack[in_open]);update_terminal; +state=new_line; +if (name==str_ptr-1) /*conserve string pool space (but see note above)*/ + {@+flush_string;name=cur_name; + } +@<Read the first line of the new file@>; +} + +@ Here we have to remember to tell the |input_ln| routine not to +start with a |get|. If the file is empty, it is considered to +contain a single blank line. +@^system dependencies@> +@^empty line at end of file@> + +@<Read the first line...@>= +{@+line=1; +if (input_ln(&cur_file, false)) do_nothing; +firm_up_the_line(); +if (end_line_char_inactive) decr(limit); +else buffer[limit]=end_line_char; +first=limit+1;loc=start; +} + +@* Font metric data. +\TeX\ gets its knowledge about fonts from font metric files, also called +\.{TFM} files; the `\.T' in `\.{TFM}' stands for \TeX, +but other programs know about them too. +@:TFM files}{\.{TFM} files@> +@^font metric files@> + +The information in a \.{TFM} file appears in a sequence of 8-bit bytes. +Since the number of bytes is always a multiple of 4, we could +also regard the file as a sequence of 32-bit words, but \TeX\ uses the +byte interpretation. The format of \.{TFM} files was designed by +Lyle Ramshaw in 1980. The intent is to convey a lot of different kinds +@^Ramshaw, Lyle Harold@> +of information in a compact but useful form. + +@<Glob...@>= +static byte_file @!tfm_file; + +@ The first 24 bytes (6 words) of a \.{TFM} file contain twelve 16-bit +integers that give the lengths of the various subsequent portions +of the file. These twelve integers are, in order: +$$\vbox{\halign{\hfil#&$\null=\null$#\hfil\cr +|lf|&length of the entire file, in words;\cr +|lh|&length of the header data, in words;\cr +|bc|&smallest character code in the font;\cr +|ec|&largest character code in the font;\cr +|nw|&number of words in the width table;\cr +|nh|&number of words in the height table;\cr +|nd|&number of words in the depth table;\cr +|ni|&number of words in the italic correction table;\cr +|nl|&number of words in the lig/kern table;\cr +|nk|&number of words in the kern table;\cr +|ne|&number of words in the extensible character table;\cr +|np|&number of font parameter words.\cr}}$$ +They are all nonnegative and less than $2^{15}$. We must have |bc-1 <= ec <= 255|, +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} +\def\PB#1{\arr#1} +\vbox{\halign{\hfil\\{#}&$\,:\,$#\hfil\cr +header&|[0 dotdot lh-1]@t\\{stuff}@>|\cr +char\_info&|[bc dotdot ec]char_info_word|\cr +width&|[0 dotdot nw-1]fix_word|\cr +height&|[0 dotdot nh-1]fix_word|\cr +depth&|[0 dotdot nd-1]fix_word|\cr +italic&|[0 dotdot ni-1]fix_word|\cr +lig\_kern&|[0 dotdot nl-1]lig_kern_command|\cr +kern&|[0 dotdot nk-1]fix_word|\cr +exten&|[0 dotdot ne-1]extensible_recipe|\cr +param&|[1 dotdot np]fix_word|\cr}}$$ +The most important data type used here is a |@!fix_word|, which is +a 32-bit representation of a binary fraction. A |fix_word| is a signed +quantity, with the two's complement of the entire word used to represent +negation. Of the 32 bits in a |fix_word|, exactly 12 are to the left of the +binary point; thus, the largest |fix_word| value is $2048-2^{-20}$, and +the smallest is $-2048$. We will see below, however, that all but two of +the |fix_word| values must lie between $-16$ and $+16$. + +@ The first data array is a block of header information, which contains +general facts about the font. The header must contain at least two words, +|header[0]| and |header[1]|, whose meaning is explained below. +Additional header information of use to other software routines might +also be included, but \TeX82 does not need to know about such details. +For example, 16 more words of header information are in use at the Xerox +Palo Alto Research Center; the first ten specify the character coding +scheme used (e.g., `\.{XEROX text}' or `\.{TeX math symbols}'), the next five +give the font identifier (e.g., `\.{HELVETICA}' or `\.{CMSY}'), and the +last gives the ``face byte.'' The program that converts \.{DVI} files +to Xerox printing format gets this information by looking at the \.{TFM} +file, which it needs to read anyway because of other information that +is not explicitly repeated in \.{DVI}~format. + +\yskip\hang|header[0]| is a 32-bit check sum that \TeX\ will copy into +the \.{DVI} output file. Later on when the \.{DVI} file is printed, +possibly on another computer, the actual font that gets used is supposed +to have a check sum that agrees with the one in the \.{TFM} file used by +\TeX. In this way, users will be warned about potential incompatibilities. +(However, if the check sum is zero in either the font file or the \.{TFM} +file, no check is made.) The actual relation between this check sum and +the rest of the \.{TFM} file is not important; the check sum is simply an +identification number with the property that incompatible fonts almost +always have distinct check sums. +@^check sum@> + +\yskip\hang|header[1]| is a |fix_word| containing the design size of +the font, in units of \TeX\ points. This number must be at least 1.0; it is +fairly arbitrary, but usually the design size is 10.0 for a ``10 point'' +font, i.e., a font that was designed to look best at a 10-point size, +whatever that really means. When a \TeX\ user asks for a font +`\.{at} $\delta$ \.{pt}', the effect is to override the design size +and replace it by $\delta$, and to multiply the $x$ and~$y$ coordinates +of the points in the font image by a factor of $\delta$ divided by the +design size. {\sl All other dimensions in the\/ \.{TFM} file are +|fix_word|\kern-1pt\ numbers in design-size units}, with the exception of +|param[1]| (which denotes the slant ratio). Thus, for example, the value +of |param[6]|, which defines the \.{em} unit, is often the |fix_word| value +$2^{20}=1.0$, since many fonts have a design size equal to one em. +The other dimensions must be less than 16 design-size units in absolute +value; thus, |header[1]| and |param[1]| are the only |fix_word| +entries in the whole \.{TFM} file whose first byte might be something +besides 0 or 255. + +@ Next comes the |char_info| array, which contains one |@!char_info_word| +per character. Each word in this part of the file contains six fields +packed into four bytes as follows. + +\yskip\hang first byte: |@!width_index| (8 bits)\par +\hang second byte: |@!height_index| (4 bits) times 16, plus |@!depth_index| + (4~bits)\par +\hang third byte: |@!italic_index| (6 bits) times 4, plus |@!tag| + (2~bits)\par +\hang fourth byte: |@!rem| (8 bits)\par +\yskip\noindent +The actual width of a character is \\{width}|[width_index]|, in design-size +units; this is a device for compressing information, since many characters +have the same width. Since it is quite common for many characters +to have the same height, depth, or italic correction, the \.{TFM} format +imposes a limit of 16 different heights, 16 different depths, and +64 different italic corrections. + +@!@^italic correction@> +The italic correction of a character has two different uses. +(a)~In ordinary text, the italic correction is added to the width only if +the \TeX\ user specifies `\.{\\/}' after the character. +(b)~In math formulas, the italic correction is always added to the width, +except with respect to the positioning of subscripts. + +Incidentally, the relation $\\{width}[0]=\\{height}[0]=\\{depth}[0]= +\\{italic}[0]=0$ should always hold, so that an index of zero implies a +value of zero. The |width_index| should never be zero unless the +character does not exist in the font, since a character is valid if and +only if it lies between |bc| and |ec| and has a nonzero |width_index|. + +@ The |tag| field in a |char_info_word| has four values that explain how to +interpret the |rem| field. + +\yskip\hangg|tag==0| (|no_tag|) means that |rem| is unused.\par +\hangg|tag==1| (|lig_tag|) means that this character has a ligature/kerning +program starting at position |rem| in the |lig_kern| array.\par +\hangg|tag==2| (|list_tag|) means that this character is part of a chain of +characters of ascending sizes, and not the largest in the chain. The +|rem| field gives the character code of the next larger character.\par +\hangg|tag==3| (|ext_tag|) means that this character code represents an +extensible character, i.e., a character that is built up of smaller pieces +so that it can be made arbitrarily large. The pieces are specified in +|@!exten[rem]|.\par +\yskip\noindent +Characters with |tag==2| and |tag==3| are treated as characters with |tag==0| +unless they are used in special circumstances in math formulas. For example, +the \.{\\sum} operation looks for a |list_tag|, and the \.{\\left} +operation looks for both |list_tag| and |ext_tag|. + +@d no_tag 0 /*vanilla character*/ +@d lig_tag 1 /*character has a ligature/kerning program*/ +@d list_tag 2 /*character has a successor in a charlist*/ +@d ext_tag 3 /*character is extensible*/ + +@ The |lig_kern| array contains instructions in a simple programming language +that explains what to do for special letter pairs. Each word in this array is a +|@!lig_kern_command| of four bytes. + +\yskip\hang first byte: |skip_byte|, indicates that this is the final program + step if the byte is 128 or more, otherwise the next step is obtained by + skipping this number of intervening steps.\par +\hang second byte: |next_char|, ``if |next_char| follows the current character, + then perform the operation and stop, otherwise continue.''\par +\hang third byte: |op_byte|, indicates a ligature step if less than~128, + a kern step otherwise.\par +\hang fourth byte: |rem|.\par +\yskip\noindent +In a kern step, an +additional space equal to |kern[256*(op_byte-128)+rem]| 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 +|rem| 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 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 boundary character at the +left, beginning at location |256*op_byte+rem|. +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+rem|. 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+rem < nl|.}$$ +If such an instruction is encountered during +normal program execution, it denotes an unconditional halt; no ligature +or kerning command is performed. + +@d stop_flag qi(128) /*value indicating `\.{STOP}' in a lig/kern program*/ +@d kern_flag qi(128) /*op code for a kern step*/ +@d skip_byte(A) A.b0 +@d next_char(A) A.b1 +@d op_byte(A) A.b2 +@d rem_byte(A) A.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(A) A.b0 /*|top| piece in a recipe*/ +@d ext_mid(A) A.b1 /*|mid| piece in a recipe*/ +@d ext_bot(A) A.b2 /*|bot| piece in a recipe*/ +@d ext_rep(A) A.b3 /*|rep| piece in a recipe*/ + +@ The final portion of a \.{TFM} file is the |param| array, which is another +sequence of |fix_word| values. + +\yskip\hang|param[1]==slant| is the amount of italic slant, which is used +to help position accents. For example, |slant==.25| means that when you go +up one unit, you also go .25 units to the right. The |slant| is a pure +number; it's the only |fix_word| other than the design size itself that is +not scaled by the design size. + +\hang|param[2]==space| is the normal spacing between words in text. +Note that character |' '| in the font need not have anything to do with +blank spaces. + +\hang|param[3]==space_stretch| is the amount of glue stretching between words. + +\hang|param[4]==space_shrink| is the amount of glue shrinking between words. + +\hang|param[5]==x_height| is the size of one ex in the font; it is also +the height of letters for which accents don't have to be raised or lowered. + +\hang|param[6]==quad| is the size of one em in the font. + +\hang|param[7]==extra_space| is the amount added to |param[2]| at the +ends of sentences. + +\yskip\noindent +If fewer than seven parameters are present, \TeX\ sets the missing parameters +to zero. Fonts used for math symbols are required to have +additional parameter information, which is explained later. + +@d slant_code 1 +@d space_code 2 +@d space_stretch_code 3 +@d space_shrink_code 4 +@d x_height_code 5 +@d quad_code 6 +@d extra_space_code 7 + +@ So that is what \.{TFM} files hold. Since \TeX\ has to absorb such information +about lots of fonts, it stores most of the data in a large array called +|font_info|. Each item of |font_info| is a |memory_word|; the |fix_word| +data gets converted into |scaled| entries, while everything else goes into +words of type |four_quarters|. + +When the user defines \.{\\font\\f}, say, \TeX\ assigns an internal number +to the user's font~\.{\\f}. Adding this number to |font_id_base| gives the +|eqtb| location of a ``frozen'' control sequence that will always select +the font. + +@<Types...@>= +typedef uint8_t internal_font_number; /*|font| in a |char_node|*/ +typedef int32_t font_index; /*index into |font_info|*/ + +@ Here now is the (rather formidable) array of font arrays. + +@d non_char qi(256) /*a |halfword| code that can't match a real character*/ +@d non_address 0 /*a spurious |bchar_label|*/ + +@<Glob...@>= +static memory_word @!font_info[font_mem_size+1]; + /*the big collection of font data*/ +static font_index @!fmem_ptr; /*first unused word of |font_info|*/ +static internal_font_number @!font_ptr; /*largest internal font number in use*/ +static four_quarters @!font_check0[font_max-font_base+1], + *const @!font_check = @!font_check0-font_base; /*check sum*/ +static scaled @!font_size0[font_max-font_base+1], + *const @!font_size = @!font_size0-font_base; /*``at'' size*/ +static scaled @!font_dsize0[font_max-font_base+1], + *const @!font_dsize = @!font_dsize0-font_base; /*``design'' size*/ +static font_index @!font_params0[font_max-font_base+1], + *const @!font_params = @!font_params0-font_base; /*how many font + parameters are present*/ +static str_number @!font_name0[font_max-font_base+1], + *const @!font_name = @!font_name0-font_base; /*name of the font*/ +static str_number @!font_area0[font_max-font_base+1], + *const @!font_area = @!font_area0-font_base; /*area of the font*/ +static eight_bits @!font_bc0[font_max-font_base+1], + *const @!font_bc = @!font_bc0-font_base; + /*beginning (smallest) character code*/ +static eight_bits @!font_ec0[font_max-font_base+1], + *const @!font_ec = @!font_ec0-font_base; + /*ending (largest) character code*/ +static pointer @!font_glue0[font_max-font_base+1], + *const @!font_glue = @!font_glue0-font_base; + /*glue specification for interword space, |null| if not allocated*/ +static bool @!font_used0[font_max-font_base+1], + *const @!font_used = @!font_used0-font_base; + /*has a character from this font actually appeared in the output?*/ +static int @!hyphen_char0[font_max-font_base+1], + *const @!hyphen_char = @!hyphen_char0-font_base; + /*current \.{\\hyphenchar} values*/ +static int @!skew_char0[font_max-font_base+1], + *const @!skew_char = @!skew_char0-font_base; + /*current \.{\\skewchar} values*/ +static font_index @!bchar_label0[font_max-font_base+1], + *const @!bchar_label = @!bchar_label0-font_base; + /*start of |lig_kern| program for left boundary character, + |non_address| if there is none*/ +static int16_t @!font_bchar0[font_max-font_base+1], + *const @!font_bchar = @!font_bchar0-font_base; + /*boundary character, |non_char| if there is none*/ +static int16_t @!font_false_bchar0[font_max-font_base+1], + *const @!font_false_bchar = @!font_false_bchar0-font_base; + /*|font_bchar| if it doesn't exist in the font, otherwise |non_char|*/ + +@ Besides the arrays just enumerated, we have directory arrays that make it +easy to get at the individual entries in |font_info|. For example, the +|char_info| data for character |c| in font |f| will be in +|font_info[char_base[f]+c].qqqq|; and if |w| is the |width_index| +part of this word (the |b0| field), the width of the character is +|font_info[width_base[f]+w].sc|. (These formulas assume that +|min_quarterword| has already been added to |c| and to |w|, since \TeX\ +stores its quarterwords that way.) + +@<Glob...@>= +static int @!char_base0[font_max-font_base+1], + *const @!char_base = @!char_base0-font_base; + /*base addresses for |char_info|*/ +static int @!width_base0[font_max-font_base+1], + *const @!width_base = @!width_base0-font_base; + /*base addresses for widths*/ +static int @!height_base0[font_max-font_base+1], + *const @!height_base = @!height_base0-font_base; + /*base addresses for heights*/ +static int @!depth_base0[font_max-font_base+1], + *const @!depth_base = @!depth_base0-font_base; + /*base addresses for depths*/ +static int @!italic_base0[font_max-font_base+1], + *const @!italic_base = @!italic_base0-font_base; + /*base addresses for italic corrections*/ +static int @!lig_kern_base0[font_max-font_base+1], + *const @!lig_kern_base = @!lig_kern_base0-font_base; + /*base addresses for ligature/kerning programs*/ +static int @!kern_base0[font_max-font_base+1], + *const @!kern_base = @!kern_base0-font_base; + /*base addresses for kerns*/ +static int @!exten_base0[font_max-font_base+1], + *const @!exten_base = @!exten_base0-font_base; + /*base addresses for extensible recipes*/ +static int @!param_base0[font_max-font_base+1], + *const @!param_base = @!param_base0-font_base; + /*base addresses for font parameters*/ + +@ @<Set init...@>= +for (k=font_base; k<=font_max; k++) font_used[k]=false; + +@ \TeX\ always knows at least one font, namely the null font. It has no +characters, and its seven parameters are all equal to zero. + +@<Initialize table...@>= +font_ptr=null_font;fmem_ptr=7; +font_name[null_font]=s_no("nullfont");font_area[null_font]=empty_string; +hyphen_char[null_font]='-';skew_char[null_font]=-1; +bchar_label[null_font]=non_address; +font_bchar[null_font]=non_char;font_false_bchar[null_font]=non_char; +font_bc[null_font]=1;font_ec[null_font]=0; +font_size[null_font]=0;font_dsize[null_font]=0; +char_base[null_font]=0;width_base[null_font]=0; +height_base[null_font]=0;depth_base[null_font]=0; +italic_base[null_font]=0;lig_kern_base[null_font]=0; +kern_base[null_font]=0;exten_base[null_font]=0; +font_glue[null_font]=null;font_params[null_font]=7; +param_base[null_font]=-1; +for (k=0; k<=6; k++) font_info[k].sc=0; + +@ @<Put each...@>= +primitive("nullfont", set_font, null_font); +@!@:null\_font\_}{\.{\\nullfont} primitive@> +text(frozen_null_font)=text(cur_val);eqtb[frozen_null_font]=eqtb[cur_val]; + +@ Of course we want to define macros that suppress the detail of how font +information is actually packed, so that we don't have to write things like +$$\hbox{|font_info[width_base[f]+font_info[char_base[f]+c].qqqq.b0].sc|}$$ +too often. The \.{WEB} definitions here make |char_info(f)(c)| the +|four_quarters| word of font information corresponding to character +|c| of font |f|. If |q| is such a word, |char_width(f)(q)| will be +the character's width; hence the long formula above is at least +abbreviated to +$$\hbox{|char_width(f)(char_info(f)(c))|.}$$ +Usually, of course, we will fetch |q| first and look at several of its +fields at the same time. + +The italic correction of a character will be denoted by +|char_italic(f)(q)|, so it is analogous to |char_width|. But we will get +at the height and depth in a slightly different way, since we usually want +to compute both height and depth if we want either one. The value of +|height_depth(q)| will be the 8-bit quantity +$$b=|height_index|\times16+|depth_index|,$$ and if |b| is such a byte we +will write |char_height(f)(b)| and |char_depth(f)(b)| for the height and +depth of the character |c| for which |q==char_info(f)(c)|. Got that? + +The tag field will be called |char_tag(q)|; the remainder byte will be +called |rem_byte(q)|, using a macro that we have already defined above. + +Access to a character's |width|, |height|, |depth|, and |tag| fields is +part of \TeX's inner loop, so we want these macros to produce code that is +as fast as possible under the circumstances. +@^inner loop@> + +@d char_info(A, B) font_info[char_base[A]+B].qqqq +@d char_width(A, B) font_info[width_base[A]+B.b0].sc +@d char_exists(A) (A.b0 > min_quarterword) +@d char_italic(A, B) font_info[italic_base[A]+(qo(B.b2))/4].sc +@d height_depth(A) qo(A.b1) +@d char_height(A, B) font_info[height_base[A]+(B)/16].sc +@d char_depth(A, B) font_info[depth_base[A]+(B)%16].sc +@d char_tag(A) ((qo(A.b2))%4) + +@ The global variable |null_character| is set up to be a word of +|char_info| for a character that doesn't exist. Such a word provides a +convenient way to deal with erroneous situations. + +@<Glob...@>= +static four_quarters @!null_character; /*nonexistent character information*/ + +@ @<Set init...@>= +null_character.b0=min_quarterword;null_character.b1=min_quarterword; +null_character.b2=min_quarterword;null_character.b3=min_quarterword; + +@ Here are some macros that help process ligatures and kerns. +We write |char_kern(f)(j)| to find the amount of kerning specified by +kerning command~|j| in font~|f|. If |j| is the |char_info| for a character +with a ligature/kern program, the first instruction of that program is either +|i==font_info[lig_kern_start(f)(j)]| or |font_info[lig_kern_restart(f)(i)]|, +depending on whether or not |skip_byte(i) <= stop_flag|. + +The constant |kern_base_offset| should be simplified, for \PASCAL\ compilers +that do not do local optimization. +@^system dependencies@> + +@d char_kern(A, B) font_info[kern_base[A]+256*op_byte(B)+rem_byte(B)].sc +@d kern_base_offset 256*(128+min_quarterword) +@d lig_kern_start(A, B) lig_kern_base[A]+B.b3 /*beginning of lig/kern program*/ +@d lig_kern_restart(A, B) lig_kern_base[A]+256*op_byte(B)+rem_byte(B)+32768-kern_base_offset + +@ Font parameters are referred to as |slant(f)|, |space(f)|, etc. + +@d param_end(A) param_base[A]].sc +@d param(A) font_info[A+param_end +@d slant param(slant_code) /*slant to the right, per unit distance upward*/ +@d space param(space_code) /*normal space between words*/ +@d space_stretch param(space_stretch_code) /*stretch between words*/ +@d space_shrink param(space_shrink_code) /*shrink between words*/ +@d x_height param(x_height_code) /*one ex*/ +@d quad param(quad_code) /*one em*/ +@d extra_space param(extra_space_code) /*additional space at end of sentence*/ + +@<The em width for |cur_font|@>=quad(cur_font) + +@ @<The x-height for |cur_font|@>=x_height(cur_font) + +@ \TeX\ checks the information of a \.{TFM} file for validity as the +file is being read in, so that no further checks will be needed when +typesetting is going on. The somewhat tedious subroutine that does this +is called |read_font_info|. It has four parameters: the user font +identifier~|u|, the file name and area strings |nom| and |aire|, and the +``at'' size~|s|. If |s|~is negative, it's the negative of a scale factor +to be applied to the design size; |s==-1000| is the normal case. +Otherwise |s| will be substituted for the design size; in this +case, |s| must be positive and less than $2048\rm\,pt$ +(i.e., it must be less than $2^{27}$ when considered as an integer). + +The subroutine opens and closes a global file variable called |tfm_file|. +It returns the value of the internal font number that was just loaded. +If an error is detected, an error message is issued and no font +information is stored; |null_font| is returned in this case. + +@d abort goto bad_tfm /*do this when the \.{TFM} data is wrong*/ + +@p static internal_font_number read_font_info(pointer @!u, str_number @!nom, str_number @!aire, + scaled @!s) /*input a \.{TFM} file*/ +{@+ +int k; /*index into |font_info|*/ +bool @!file_opened; /*was |tfm_file| successfully opened?*/ +halfword @!lf, @!lh, @!bc, @!ec, @!nw, @!nh, @!nd, @!ni, @!nl, @!nk, @!ne, @!np; + /*sizes of subfiles*/ +internal_font_number @!f; /*the new font's number*/ +internal_font_number @!g; /*the number to return*/ +eight_bits @!a, @!b, @!c, @!d; /*byte variables*/ +four_quarters @!qw;scaled @!sw; /*accumulators*/ +int @!bch_label; /*left boundary start location, or infinity*/ +int @!bchar; /*boundary character, or 256*/ +scaled @!z; /*the design size or the ``at'' size*/ +int @!alpha;int @!beta; + /*auxiliary quantities used in fixed-point multiplication*/ +g=null_font;@/ +@<Read and check the font data; |abort| if the \.{TFM} file is malformed; +if there's no room for this font, say so and |goto done|; otherwise |incr(font_ptr)| +and |goto done|@>; +bad_tfm: @<Report that the font won't be loaded@>; +done: if (file_opened) b_close(&tfm_file); +return g; +} + +@ There are programs called \.{TFtoPL} and \.{PLtoTF} that convert +between the \.{TFM} format and a symbolic property-list format +that can be easily edited. These programs contain extensive +diagnostic information, so \TeX\ does not have to bother giving +precise details about why it rejects a particular \.{TFM} file. +@.TFtoPL@> @.PLtoTF@> + +@d start_font_error_message print_err("Font ");sprint_cs(u); + print_char('=');print_file_name(nom, aire, empty_string); + if (s >= 0) + {@+print(" at ");print_scaled(s);print("pt"); + } + else if (s!=-1000) + {@+print(" scaled ");print_int(-s); + } + +@<Report that the font won't be loaded@>= +start_font_error_message; +@.Font x=xx not loadable...@> +if (file_opened) print(" not loadable: Bad metric (TFM) file"); +else print(" not loadable: Metric (TFM) file not found"); +help5("I wasn't able to read the size data for this font,",@/ +"so I will ignore the font specification.",@/ +"[Wizards can fix TFM files using TFtoPL/PLtoTF.]",@/ +"You might try inserting a different font spec;",@/ +"e.g., type `I\\font<same font id>=<substitute font name>'."); +error() + +@ @<Read and check...@>= +@<Open |tfm_file| for input@>; +@<Read the {\.{TFM}} size fields@>; +@<Use size fields to allocate font information@>; +@<Read the {\.{TFM}} header@>; +@<Read character data@>; +@<Read box dimensions@>; +@<Read ligature/kern program@>; +@<Read extensible character recipes@>; +@<Read font parameters@>; +@<Make final adjustments and |goto done|@>@; + +@ @<Open |tfm_file| for input@>= +file_opened=false; +pack_file_name(nom, empty_string,empty_string,".tfm"); /* \TeX\ Live */ +if (!b_open_in(&tfm_file)) abort; +file_opened=true + +@ Note: A malformed \.{TFM} file might be shorter than it claims to be; +thus |eof(tfm_file)| might be true when |read_font_info| refers to +|tfm_file.d| or when it says |get(tfm_file)|. If such circumstances +cause system error messages, you will have to defeat them somehow, +for example by defining |fget| to be `\ignorespaces|{@+get(tfm_file);| +|if (eof(tfm_file)) abort;} |\unskip'. +@^system dependencies@> + +@d fget get(tfm_file) +@d fbyte tfm_file.d +@d read_sixteen(A) {@+A=fbyte; + if (A > 127) abort; + fget;A=A*0400+fbyte; + } +@d store_four_quarters(A) {@+fget;a=fbyte;qw.b0=qi(a); + fget;b=fbyte;qw.b1=qi(b); + fget;c=fbyte;qw.b2=qi(c); + fget;d=fbyte;qw.b3=qi(d); + A=qw; + } + +@ @<Read the {\.{TFM}} size fields@>= +{@+read_sixteen(lf); +fget;read_sixteen(lh); +fget;read_sixteen(bc); +fget;read_sixteen(ec); +if ((bc > ec+1)||(ec > 255)) abort; +if (bc > 255) /*|bc==256| and |ec==255|*/ + {@+bc=1;ec=0; + } +fget;read_sixteen(nw); +fget;read_sixteen(nh); +fget;read_sixteen(nd); +fget;read_sixteen(ni); +fget;read_sixteen(nl); +fget;read_sixteen(nk); +fget;read_sixteen(ne); +fget;read_sixteen(np); +if (lf!=6+lh+(ec-bc+1)+nw+nh+nd+ni+nl+nk+ne+np) abort; +if ((nw==0)||(nh==0)||(nd==0)||(ni==0)) abort; +} + +@ The preliminary settings of the index-offset variables |char_base|, +|width_base|, |lig_kern_base|, |kern_base|, and |exten_base| will be +corrected later by subtracting |min_quarterword| from them; and we will +subtract 1 from |param_base| too. It's best to forget about such anomalies +until later. + +@<Use size fields to allocate font information@>= +lf=lf-6-lh; /*|lf| words should be loaded into |font_info|*/ +if (np < 7) lf=lf+7-np; /*at least seven parameters will appear*/ +if ((font_ptr==font_max)||(fmem_ptr+lf > font_mem_size)) + @<Apologize for not loading the font, |goto done|@>; +f=font_ptr+1; +char_base[f]=fmem_ptr-bc; +width_base[f]=char_base[f]+ec+1; +height_base[f]=width_base[f]+nw; +depth_base[f]=height_base[f]+nh; +italic_base[f]=depth_base[f]+nd; +lig_kern_base[f]=italic_base[f]+ni; +kern_base[f]=lig_kern_base[f]+nl-kern_base_offset; +exten_base[f]=kern_base[f]+kern_base_offset+nk; +param_base[f]=exten_base[f]+ne + +@ @<Apologize for not loading...@>= +{@+start_font_error_message; +print(" not loaded: Not enough room left"); +@.Font x=xx not loaded...@> +help4("I'm afraid I won't be able to make use of this font,",@/ +"because my memory for character-size data is too small.",@/ +"If you're really stuck, ask a wizard to enlarge me.",@/ +"Or maybe try `I\\font<same font id>=<name of loaded font>'."); +error();goto done; +} + +@ Only the first two words of the header are needed by \TeX82. + +@<Read the {\.{TFM}} header@>= +{@+if (lh < 2) abort; +store_four_quarters(font_check[f]); +fget;read_sixteen(z); /*this rejects a negative design size*/ +fget;z=z*0400+fbyte;fget;z=(z*020)+(fbyte/020); +if (z < unity) abort; +while (lh > 2) + {@+fget;fget;fget;fget;decr(lh); /*ignore the rest of the header*/ + } +font_dsize[f]=z; +if (s!=-1000) + if (s >= 0) z=s; + else z=xn_over_d(z,-s, 1000); +font_size[f]=z; +} + +@ @<Read character data@>= +for (k=fmem_ptr; k<=width_base[f]-1; k++) + {@+store_four_quarters(font_info[k].qqqq); + if ((a >= nw)||(b/020 >= nh)||(b%020 >= nd)|| + (c/4 >= ni)) abort; + switch (c%4) { + case lig_tag: if (d >= nl) abort;@+break; + case ext_tag: if (d >= ne) abort;@+break; + case list_tag: @<Check for charlist cycle@>@;@+break; + default:do_nothing; /*|no_tag|*/ + } + } + +@ We want to make sure that there is no cycle of characters linked together +by |list_tag| entries, since such a cycle would get \TeX\ into an endless +loop. If such a cycle exists, the routine here detects it when processing +the largest character code in the cycle. + +@d check_byte_range(A) {@+if ((A < bc)||(A > ec)) abort;@+} +@d current_character_being_worked_on k+bc-fmem_ptr + +@<Check for charlist cycle@>= +{@+check_byte_range(d); +while (d < current_character_being_worked_on) + {@+qw=char_info(f, d); + /*N.B.: not |qi(d)|, since |char_base[f]| hasn't been adjusted yet*/ + if (char_tag(qw)!=list_tag) goto not_found; + d=qo(rem_byte(qw)); /*next character on the list*/ + } +if (d==current_character_being_worked_on) abort; /*yes, there's a cycle*/ +not_found: ;} + +@ A |fix_word| whose four bytes are $(a,b,c,d)$ from left to right represents +the number +$$x=\left\{\vcenter{\halign{$#$,\hfil\qquad&if $#$\hfil\cr +b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=0;\cr +-16+b\cdot2^{-4}+c\cdot2^{-12}+d\cdot2^{-20}&a=255.\cr}}\right.$$ +(No other choices of |a| are allowed, since the magnitude of a number in +design-size units must be less than 16.) We want to multiply this +quantity by the integer~|z|, which is known to be less than $2^{27}$. +If $|z|<2^{23}$, the individual multiplications $b\cdot z$, +$c\cdot z$, $d\cdot z$ cannot overflow; otherwise we will divide |z| by 2, +4, 8, or 16, to obtain a multiplier less than $2^{23}$, and we can +compensate for this later. If |z| has thereby been replaced by +$|z|^\prime=|z|/2^e$, let $\beta=2^{4-e}$; we shall compute +$$\lfloor(b+c\cdot2^{-8}+d\cdot2^{-16})\,z^\prime/\beta\rfloor$$ +if $a=0$, or the same quantity minus $\alpha=2^{4+e}z^\prime$ if $a=255$. +This calculation must be done exactly, in order to guarantee portability +of \TeX\ between computers. + +@d store_scaled(A) {@+fget;a=fbyte;fget;b=fbyte; + fget;c=fbyte;fget;d=fbyte;@/ + sw=(((((d*z)/0400)+(c*z))/0400)+(b*z))/beta; + if (a==0) A=sw;@+else if (a==255) A=sw-alpha;@+else abort; + } + +@<Read box dimensions@>= +{@+@<Replace |z| by $|z|^\prime$ and compute $\alpha,\beta$@>; +for (k=width_base[f]; k<=lig_kern_base[f]-1; k++) + store_scaled(font_info[k].sc); +if (font_info[width_base[f]].sc!=0) abort; /*\\{width}[0] must be zero*/ +if (font_info[height_base[f]].sc!=0) abort; /*\\{height}[0] must be zero*/ +if (font_info[depth_base[f]].sc!=0) abort; /*\\{depth}[0] must be zero*/ +if (font_info[italic_base[f]].sc!=0) abort; /*\\{italic}[0] must be zero*/ +} + +@ @<Replace |z|...@>= +{@+alpha=16; +while (z >= 040000000) + {@+z=z/2;alpha=alpha+alpha; + } +beta=256/alpha;alpha=alpha*z; +} + +@ @d check_existence(A) @t@>@;@/ + {@+check_byte_range(A); + qw=char_info(f, A); /*N.B.: not |qi(A)|*/ + if (!char_exists(qw)) abort; + } + +@<Read ligature/kern program@>= +bch_label=077777;bchar=256; +if (nl > 0) + {@+for (k=lig_kern_base[f]; k<=kern_base[f]+kern_base_offset-1; k++) + {@+store_four_quarters(font_info[k].qqqq); + if (a > 128) + {@+if (256*c+d >= nl) abort; + if (a==255) if (k==lig_kern_base[f]) bchar=b; + } + else{@+if (b!=bchar) check_existence(b); + if (c < 128) check_existence(d)@; /*check ligature*/ + else if (256*(c-128)+d >= nk) abort; /*check kern*/ + if (a < 128) if (k-lig_kern_base[f]+a+1 >= nl) abort; + } + } + if (a==255) bch_label=256*c+d; + } +for (k=kern_base[f]+kern_base_offset; k<=exten_base[f]-1; k++) + store_scaled(font_info[k].sc); + +@ @<Read extensible character recipes@>= +for (k=exten_base[f]; k<=param_base[f]-1; k++) + {@+store_four_quarters(font_info[k].qqqq); + if (a!=0) check_existence(a); + if (b!=0) check_existence(b); + if (c!=0) check_existence(c); + check_existence(d); + } + +@ We check to see that the \.{TFM} file doesn't end prematurely; but +no error message is given for files having more than |lf| words. + +@<Read font parameters@>= +{@+for (k=1; k<=np; k++) + if (k==1) /*the |slant| parameter is a pure number*/ + {@+fget;sw=fbyte;if (sw > 127) sw=sw-256; + fget;sw=sw*0400+fbyte;fget;sw=sw*0400+fbyte; + fget;font_info[param_base[f]].sc= + (sw*020)+(fbyte/020); + } + else store_scaled(font_info[param_base[f]+k-1].sc); +if (eof(tfm_file)) abort; +for (k=np+1; k<=7; k++) font_info[param_base[f]+k-1].sc=0; +} + +@ Now to wrap it up, we have checked all the necessary things about the \.{TFM} +file, and all we need to do is put the finishing touches on the data for +the new font. + +@d adjust(A) A[f]=qo(A[f]) + /*correct for the excess |min_quarterword| that was added*/ + +@<Make final adjustments...@>= +if (np >= 7) font_params[f]=np;@+else font_params[f]=7; +hyphen_char[f]=default_hyphen_char;skew_char[f]=default_skew_char; +if (bch_label < nl) bchar_label[f]=bch_label+lig_kern_base[f]; +else bchar_label[f]=non_address; +font_bchar[f]=qi(bchar); +font_false_bchar[f]=qi(bchar); +if (bchar <= ec) if (bchar >= bc) + {@+qw=char_info(f, bchar); /*N.B.: not |qi(bchar)|*/ + if (char_exists(qw)) font_false_bchar[f]=non_char; + } +font_name[f]=nom; +font_area[f]=aire; +font_bc[f]=bc;font_ec[f]=ec;font_glue[f]=null; +adjust(char_base);adjust(width_base);adjust(lig_kern_base); +adjust(kern_base);adjust(exten_base); +decr(param_base[f]); +fmem_ptr=fmem_ptr+lf;font_ptr=f;g=f;goto done + +@ Before we forget about the format of these tables, let's deal with two +of \TeX's basic scanning routines related to font information. + +@<Declare procedures that scan font-related stuff@>= +static void scan_font_ident(void) +{@+internal_font_number f; +halfword @!m; +@<Get the next non-blank non-call...@>; +if (cur_cmd==def_font) f=cur_font; +else if (cur_cmd==set_font) f=cur_chr; +else if (cur_cmd==def_family) + {@+m=cur_chr;scan_four_bit_int();f=equiv(m+cur_val); + } +else{@+print_err("Missing font identifier"); +@.Missing font identifier@> + help2("I was looking for a control sequence whose",@/ + "current meaning has been defined by \\font."); + back_error();f=null_font; + } +cur_val=f; +} + +@ The following routine is used to implement `\.{\\fontdimen} |n| |f|'. +The boolean parameter |writing| is set |true| if the calling program +intends to change the parameter value. + +@<Declare procedures that scan font-related stuff@>= +static void find_font_dimen(bool @!writing) + /*sets |cur_val| to |font_info| location*/ +{@+internal_font_number f; +int @!n; /*the parameter number*/ +scan_int();n=cur_val;scan_font_ident();f=cur_val; +if (n <= 0) cur_val=fmem_ptr; +else{@+if (writing&&(n <= space_shrink_code)&&@| + (n >= space_code)&&(font_glue[f]!=null)) + {@+delete_glue_ref(font_glue[f]); + font_glue[f]=null; + } + if (n > font_params[f]) + if (f < font_ptr) cur_val=fmem_ptr; + else@<Increase the number of parameters in the last font@>@; + else cur_val=n+param_base[f]; + } +@<Issue an error message if |cur_val=fmem_ptr|@>; +} + +@ @<Issue an error message if |cur_val=fmem_ptr|@>= +if (cur_val==fmem_ptr) + {@+print_err("Font ");printn_esc(font_id_text(f)); + print(" has only ");print_int(font_params[f]); + print(" fontdimen parameters"); +@.Font x has only...@> + help2("To increase the number of font parameters, you must",@/ + "use \\fontdimen immediately after the \\font is loaded."); + error(); + } + +@ @<Increase the number of parameters...@>= +{@+@/do@+{if (fmem_ptr==font_mem_size) + overflow("font memory", font_mem_size); +@:TeX capacity exceeded font memory}{\quad font memory@> +font_info[fmem_ptr].sc=0;incr(fmem_ptr);incr(font_params[f]); +}@+ while (!(n==font_params[f])); +cur_val=fmem_ptr-1; /*this equals |param_base[f]+font_params[f]|*/ +} + +@ When \TeX\ wants to typeset a character that doesn't exist, the +character node is not created; thus the output routine can assume +that characters exist when it sees them. The following procedure +prints a warning message unless the user has suppressed it. + +@p static void char_warning(internal_font_number @!f, eight_bits @!c) +{@+int old_setting; /*saved value of |tracing_online|*/ +if (tracing_lost_chars > 0) + {@+old_setting=tracing_online; + if (eTeX_ex&&(tracing_lost_chars > 1)) tracing_online=1; + {@+begin_diagnostic(); + print_nl("Missing character: There is no "); +@.Missing character@> + print_ASCII(c);print(" in font "); + slow_print(font_name[f]);print_char('!');end_diagnostic(false); + } + tracing_online=old_setting; + } +} + +@ Here is a function that returns a pointer to a character node for a +given character in a given font. If that character doesn't exist, +|null| is returned instead. + +@p static pointer new_character(internal_font_number @!f, eight_bits @!c) +{@+ +pointer p; /*newly allocated node*/ +if (font_bc[f] <= c) if (font_ec[f] >= c) + if (char_exists(char_info(f, qi(c)))) + {@+p=get_avail();font(p)=f;character(p)=qi(c); + return p; + } +char_warning(f, c); +return null; +} + +@* Device-independent file format. +The most important output produced by a run of \TeX\ is the ``device +independent'' (\.{DVI}) file that specifies where characters and rules +are to appear on printed pages. The form of these files was designed by +David R. Fuchs in 1979. Almost any reasonable typesetting device can be +@^Fuchs, David Raymond@> +@:DVI\_files}{\.{DVI} files@> +driven by a program that takes \.{DVI} files as input, and dozens of such +\.{DVI}-to-whatever programs have been written. Thus, it is possible to +print the output of \TeX\ on many different kinds of equipment, using \TeX\ +as a device-independent ``front end.'' + +A \.{DVI} file is a stream of 8-bit bytes, which may be regarded as a +series of commands in a machine-like language. The first byte of each command +is the operation code, and this code is followed by zero or more bytes +that provide parameters to the command. The parameters themselves may consist +of several consecutive bytes; for example, the `|set_rule|' command has two +parameters, each of which is four bytes long. Parameters are usually +regarded as nonnegative integers; but four-byte-long parameters, +and shorter parameters that denote distances, can be +either positive or negative. Such parameters are given in two's complement +notation. For example, a two-byte-long distance parameter has a value between +$-2^{15}$ and $2^{15}-1$. As in \.{TFM} files, numbers that occupy +more than one byte position appear in BigEndian order. + +A \.{DVI} file consists of a ``preamble,'' followed by a sequence of one +or more ``pages,'' followed by a ``postamble.'' The preamble is simply a +|pre| command, with its parameters that define the dimensions used in the +file; this must come first. Each ``page'' consists of a |bop| command, +followed by any number of other commands that tell where characters are to +be placed on a physical page, followed by an |eop| command. The pages +appear in the order that \TeX\ generated them. If we ignore |nop| commands +and \\{fnt\_def} commands (which are allowed between any two commands in +the file), each |eop| command is immediately followed by a |bop| command, +or by a |post| command; in the latter case, there are no more pages in the +file, and the remaining bytes form the postamble. Further details about +the postamble will be explained later. + +Some parameters in \.{DVI} commands are ``pointers.'' These are four-byte +quantities that give the location number of some other byte in the file; +the first byte is number~0, then comes number~1, and so on. For example, +one of the parameters of a |bop| command points to the previous |bop|; +this makes it feasible to read the pages in backwards order, in case the +results are being directed to a device that stacks its output face up. +Suppose the preamble of a \.{DVI} file occupies bytes 0 to 99. Now if the +first page occupies bytes 100 to 999, say, and if the second +page occupies bytes 1000 to 1999, then the |bop| that starts in byte 1000 +points to 100 and the |bop| that starts in byte 2000 points to 1000. (The +very first |bop|, i.e., the one starting in byte 100, has a pointer of~$-1$.) + +@ The \.{DVI} format is intended to be both compact and easily interpreted +by a machine. Compactness is achieved by making most of the information +implicit instead of explicit. When a \.{DVI}-reading program reads the +commands for a page, it keeps track of several quantities: (a)~The current +font |f| is an integer; this value is changed only +by \\{fnt} and \\{fnt\_num} commands. (b)~The current position on the page +is given by two numbers called the horizontal and vertical coordinates, +|h| and |v|. Both coordinates are zero at the upper left corner of the page; +moving to the right corresponds to increasing the horizontal coordinate, and +moving down corresponds to increasing the vertical coordinate. Thus, the +coordinates are essentially Cartesian, except that vertical directions are +flipped; the Cartesian version of |(h, v)| would be |(h,-v)|. (c)~The +current spacing amounts are given by four numbers |w|, |x|, |y|, and |z|, +where |w| and~|x| are used for horizontal spacing and where |y| and~|z| +are used for vertical spacing. (d)~There is a stack containing +|(h, v, w, x, y, z)| values; the \.{DVI} commands |push| and |pop| are used to +change the current level of operation. Note that the current font~|f| is +not pushed and popped; the stack contains only information about +positioning. + +The values of |h|, |v|, |w|, |x|, |y|, and |z| are signed integers having up +to 32 bits, including the sign. Since they represent physical distances, +there is a small unit of measurement such that increasing |h| by~1 means +moving a certain tiny distance to the right. The actual unit of +measurement is variable, as explained below; \TeX\ sets things up so that +its \.{DVI} output is in sp units, i.e., scaled points, in agreement with +all the |scaled| dimensions in \TeX's data structures. + +@ Here is a list of all the commands that may appear in a \.{DVI} file. Each +command is specified by its symbolic name (e.g., |bop|), its opcode byte +(e.g., 139), and its parameters (if any). The parameters are followed +by a bracketed number telling how many bytes they occupy; for example, +`|p[4]|' means that parameter |p| is four bytes long. + +\yskip\hang|set_char_0| 0. Typeset character number~0 from font~|f| +such that the reference point of the character is at |(h, v)|. Then +increase |h| by the width of that character. Note that a character may +have zero or negative width, so one cannot be sure that |h| will advance +after this command; but |h| usually does increase. + +\yskip\hang\\{set\_char\_1} through \\{set\_char\_127} (opcodes 1 to 127). +Do the operations of |set_char_0|; but use the character whose number +matches the opcode, instead of character~0. + +\yskip\hang|set1| 128 |c[1]|. Same as |set_char_0|, except that character +number~|c| is typeset. \TeX82 uses this command for characters in the +range |128 <= c < 256|. + +\yskip\hang|@!set2| 129 |c[2]|. Same as |set1|, except that |c|~is two +bytes long, so it is in the range |0 <= c < 65536|. \TeX82 never uses this +command, but it should come in handy for extensions of \TeX\ that deal +with oriental languages. +@^oriental characters@>@^Chinese characters@>@^Japanese characters@> + +\yskip\hang|@!set3| 130 |c[3]|. Same as |set1|, except that |c|~is three +bytes long, so it can be as large as $2^{24}-1$. Not even the Chinese +language has this many characters, but this command might prove useful +in some yet unforeseen extension. + +\yskip\hang|@!set4| 131 |c[4]|. Same as |set1|, except that |c|~is four +bytes long. Imagine that. + +\yskip\hang|set_rule| 132 |a[4]| |b[4]|. Typeset a solid black rectangle +of height~|a| and width~|b|, with its bottom left corner at |(h, v)|. Then +set |h=h+b|. If either |a <= 0| or |b <= 0|, nothing should be typeset. Note +that if |b < 0|, the value of |h| will decrease even though nothing else happens. +See below for details about how to typeset rules so that consistency with +\MF\ is guaranteed. + +\yskip\hang|@!put1| 133 |c[1]|. Typeset character number~|c| from font~|f| +such that the reference point of the character is at |(h, v)|. (The `put' +commands are exactly like the `set' commands, except that they simply put out a +character or a rule without moving the reference point afterwards.) + +\yskip\hang|@!put2| 134 |c[2]|. Same as |set2|, except that |h| is not changed. + +\yskip\hang|@!put3| 135 |c[3]|. Same as |set3|, except that |h| is not changed. + +\yskip\hang|@!put4| 136 |c[4]|. Same as |set4|, except that |h| is not changed. + +\yskip\hang|put_rule| 137 |a[4]| |b[4]|. Same as |set_rule|, except that +|h| is not changed. + +\yskip\hang|nop| 138. No operation, do nothing. Any number of |nop|'s +may occur between \.{DVI} commands, but a |nop| cannot be inserted between +a command and its parameters or between two parameters. + +\yskip\hang|bop| 139 $c_0[4]$ $c_1[4]$ $\ldots$ $c_9[4]$ $p[4]$. Beginning +of a page: Set |(h, v, w, x, y, z)=(0, 0, 0, 0, 0, 0)| and set the stack empty. Set +the current font |f| to an undefined value. The ten $c_i$ parameters hold +the values of \.{\\count0} $\ldots$ \.{\\count9} in \TeX\ at the time +\.{\\shipout} was invoked for this page; they can be used to identify +pages, if a user wants to print only part of a \.{DVI} file. The parameter +|p| points to the previous |bop| in the file; the first +|bop| has $p=-1$. + +\yskip\hang|eop| 140. End of page: Print what you have read since the +previous |bop|. At this point the stack should be empty. (The \.{DVI}-reading +programs that drive most output devices will have kept a buffer of the +material that appears on the page that has just ended. This material is +largely, but not entirely, in order by |v| coordinate and (for fixed |v|) by +|h|~coordinate; so it usually needs to be sorted into some order that is +appropriate for the device in question.) + +\yskip\hang|push| 141. Push the current values of |(h, v, w, x, y, z)| onto the +top of the stack; do not change any of these values. Note that |f| is +not pushed. + +\yskip\hang|pop| 142. Pop the top six values off of the stack and assign +them respectively to |(h, v, w, x, y, z)|. The number of pops should never +exceed the number of pushes, since it would be highly embarrassing if the +stack were empty at the time of a |pop| command. + +\yskip\hang|right1| 143 |b[1]|. Set |h=h+b|, i.e., move right |b| units. +The parameter is a signed number in two's complement notation, |-128 <= b < 128|; +if |b < 0|, the reference point moves left. + +\yskip\hang|@!right2| 144 |b[2]|. Same as |right1|, except that |b| is a +two-byte quantity in the range |-32768 <= b < 32768|. + +\yskip\hang|@!right3| 145 |b[3]|. Same as |right1|, except that |b| is a +three-byte quantity in the range |@t$-2^{23}$@> <= b < @t$2^{23}$@>|. + +\yskip\hang|@!right4| 146 |b[4]|. Same as |right1|, except that |b| is a +four-byte quantity in the range |@t$-2^{31}$@> <= b < @t$2^{31}$@>|. + +\yskip\hang|w0| 147. Set |h=h+w|; i.e., move right |w| units. With luck, +this parameterless command will usually suffice, because the same kind of motion +will occur several times in succession; the following commands explain how +|w| gets particular values. + +\yskip\hang|w1| 148 |b[1]|. Set |w=b| and |h=h+b|. The value of |b| is a +signed quantity in two's complement notation, |-128 <= b < 128|. This command +changes the current |w|~spacing and moves right by |b|. + +\yskip\hang|@!w2| 149 |b[2]|. Same as |w1|, but |b| is two bytes long, +|-32768 <= b < 32768|. + +\yskip\hang|@!w3| 150 |b[3]|. Same as |w1|, but |b| is three bytes long, +|@t$-2^{23}$@> <= b < @t$2^{23}$@>|. + +\yskip\hang|@!w4| 151 |b[4]|. Same as |w1|, but |b| is four bytes long, +|@t$-2^{31}$@> <= b < @t$2^{31}$@>|. + +\yskip\hang|x0| 152. Set |h=h+x|; i.e., move right |x| units. The `|x|' +commands are like the `|w|' commands except that they involve |x| instead +of |w|. + +\yskip\hang|x1| 153 |b[1]|. Set |x=b| and |h=h+b|. The value of |b| is a +signed quantity in two's complement notation, |-128 <= b < 128|. This command +changes the current |x|~spacing and moves right by |b|. + +\yskip\hang|@!x2| 154 |b[2]|. Same as |x1|, but |b| is two bytes long, +|-32768 <= b < 32768|. + +\yskip\hang|@!x3| 155 |b[3]|. Same as |x1|, but |b| is three bytes long, +|@t$-2^{23}$@> <= b < @t$2^{23}$@>|. + +\yskip\hang|@!x4| 156 |b[4]|. Same as |x1|, but |b| is four bytes long, +|@t$-2^{31}$@> <= b < @t$2^{31}$@>|. + +\yskip\hang|down1| 157 |a[1]|. Set |v=v+a|, i.e., move down |a| units. +The parameter is a signed number in two's complement notation, |-128 <= a < 128|; +if |a < 0|, the reference point moves up. + +\yskip\hang|@!down2| 158 |a[2]|. Same as |down1|, except that |a| is a +two-byte quantity in the range |-32768 <= a < 32768|. + +\yskip\hang|@!down3| 159 |a[3]|. Same as |down1|, except that |a| is a +three-byte quantity in the range |@t$-2^{23}$@> <= a < @t$2^{23}$@>|. + +\yskip\hang|@!down4| 160 |a[4]|. Same as |down1|, except that |a| is a +four-byte quantity in the range |@t$-2^{31}$@> <= a < @t$2^{31}$@>|. + +\yskip\hang|y0| 161. Set |v=v+y|; i.e., move down |y| units. With luck, +this parameterless command will usually suffice, because the same kind of motion +will occur several times in succession; the following commands explain how +|y| gets particular values. + +\yskip\hang|y1| 162 |a[1]|. Set |y=a| and |v=v+a|. The value of |a| is a +signed quantity in two's complement notation, |-128 <= a < 128|. This command +changes the current |y|~spacing and moves down by |a|. + +\yskip\hang|@!y2| 163 |a[2]|. Same as |y1|, but |a| is two bytes long, +|-32768 <= a < 32768|. + +\yskip\hang|@!y3| 164 |a[3]|. Same as |y1|, but |a| is three bytes long, +|@t$-2^{23}$@> <= a < @t$2^{23}$@>|. + +\yskip\hang|@!y4| 165 |a[4]|. Same as |y1|, but |a| is four bytes long, +|@t$-2^{31}$@> <= a < @t$2^{31}$@>|. + +\yskip\hang|z0| 166. Set |v=v+z|; i.e., move down |z| units. The `|z|' commands +are like the `|y|' commands except that they involve |z| instead of |y|. + +\yskip\hang|z1| 167 |a[1]|. Set |z=a| and |v=v+a|. The value of |a| is a +signed quantity in two's complement notation, |-128 <= a < 128|. This command +changes the current |z|~spacing and moves down by |a|. + +\yskip\hang|@!z2| 168 |a[2]|. Same as |z1|, but |a| is two bytes long, +|-32768 <= a < 32768|. + +\yskip\hang|@!z3| 169 |a[3]|. Same as |z1|, but |a| is three bytes long, +|@t$-2^{23}$@> <= a < @t$2^{23}$@>|. + +\yskip\hang|@!z4| 170 |a[4]|. Same as |z1|, but |a| is four bytes long, +|@t$-2^{31}$@> <= a < @t$2^{31}$@>|. + +\yskip\hang|fnt_num_0| 171. Set |f=0|. Font 0 must previously have been +defined by a \\{fnt\_def} instruction, as explained below. + +\yskip\hang\\{fnt\_num\_1} through \\{fnt\_num\_63} (opcodes 172 to 234). Set +|f=1|, \dots, \hbox{|f=63|}, respectively. + +\yskip\hang|fnt1| 235 |k[1]|. Set |f=k|. \TeX82 uses this command for font +numbers in the range |64 <= k < 256|. + +\yskip\hang|@!fnt2| 236 |k[2]|. Same as |fnt1|, except that |k|~is two +bytes long, so it is in the range |0 <= k < 65536|. \TeX82 never generates this +command, but large font numbers may prove useful for specifications of +color or texture, or they may be used for special fonts that have fixed +numbers in some external coding scheme. + +\yskip\hang|@!fnt3| 237 |k[3]|. Same as |fnt1|, except that |k|~is three +bytes long, so it can be as large as $2^{24}-1$. + +\yskip\hang|@!fnt4| 238 |k[4]|. Same as |fnt1|, except that |k|~is four +bytes long; this is for the really big font numbers (and for the negative ones). + +\yskip\hang|xxx1| 239 |k[1]| |x[k]|. This command is undefined in +general; it functions as a $(k+2)$-byte |nop| unless special \.{DVI}-reading +programs are being used. \TeX82 generates |xxx1| when a short enough +\.{\\special} appears, setting |k| to the number of bytes being sent. It +is recommended that |x| be a string having the form of a keyword followed +by possible parameters relevant to that keyword. + +\yskip\hang|@!xxx2| 240 |k[2]| |x[k]|. Like |xxx1|, but |0 <= k < 65536|. + +\yskip\hang|@!xxx3| 241 |k[3]| |x[k]|. Like |xxx1|, but |0 <= k < @t$2^{24}$@>|. + +\yskip\hang|xxx4| 242 |k[4]| |x[k]|. Like |xxx1|, but |k| can be ridiculously +large. \TeX82 uses |xxx4| when sending a string of length 256 or more. + +\yskip\hang|fnt_def1| 243 |k[1]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0 <= k < 256|; font definitions will be explained shortly. + +\yskip\hang|@!fnt_def2| 244 |k[2]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0 <= k < 65536|. + +\yskip\hang|@!fnt_def3| 245 |k[3]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |0 <= k < @t$2^{24}$@>|. + +\yskip\hang|@!fnt_def4| 246 |k[4]| |c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|. +Define font |k|, where |@t$-2^{31}$@> <= k < @t$2^{31}$@>|. + +\yskip\hang|pre| 247 |i[1]| |num[4]| |den[4]| |mag[4]| |k[1]| |x[k]|. +Beginning of the preamble; this must come at the very beginning of the +file. Parameters |i|, |num|, |den|, |mag|, |k|, and |x| are explained below. + +\yskip\hang|post| 248. Beginning of the postamble, see below. + +\yskip\hang|post_post| 249. Ending of the postamble, see below. + +\yskip\noindent Commands 250--255 are undefined at the present time. + +@ @d set_char_0 0 /*typeset character 0 and move right*/ +@d set1 128 /*typeset a character and move right*/ +@d set_rule 132 /*typeset a rule and move right*/ +@d put_rule 137 /*typeset a rule*/ +@d nop 138 /*no operation*/ +@d bop 139 /*beginning of page*/ +@d eop 140 /*ending of page*/ +@d push 141 /*save the current positions*/ +@d pop 142 /*restore previous positions*/ +@d right1 143 /*move right*/ +@d w0 147 /*move right by |w|*/ +@d w1 148 /*move right and set |w|*/ +@d x0 152 /*move right by |x|*/ +@d x1 153 /*move right and set |x|*/ +@d down1 157 /*move down*/ +@d y0 161 /*move down by |y|*/ +@d y1 162 /*move down and set |y|*/ +@d z0 166 /*move down by |z|*/ +@d z1 167 /*move down and set |z|*/ +@d fnt_num_0 171 /*set current font to 0*/ +@d fnt1 235 /*set current font*/ +@d xxx1 239 /*extension to \.{DVI} primitives*/ +@d xxx4 242 /*potentially long extension to \.{DVI} primitives*/ +@d fnt_def1 243 /*define the meaning of a font number*/ +@d pre 247 /*preamble*/ +@d post 248 /*postamble beginning*/ +@d post_post 249 /*postamble ending*/ + +@ The preamble contains basic information about the file as a whole. As +stated above, there are six parameters: +$$\hbox{|@!i[1]| |@!num[4]| |@!den[4]| |@!mag[4]| |@!k[1]| |@!x[k]|.}$$ +The |i| byte identifies \.{DVI} format; currently this byte is always set +to~2. (The value |i==3| is currently used for an extended format that +allows a mixture of right-to-left and left-to-right typesetting. +Some day we will set |i==4|, when \.{DVI} format makes another +incompatible change---perhaps in the year 2048.) + +The next two parameters, |num| and |den|, are positive integers that define +the units of measurement; they are the numerator and denominator of a +fraction by which all dimensions in the \.{DVI} file could be multiplied +in order to get lengths in units of $10^{-7}$ meters. Since $\rm 7227{pt} = +254{cm}$, and since \TeX\ works with scaled points where there are $2^{16}$ +sp in a point, \TeX\ sets +$|num|/|den|=(254\cdot10^5)/(7227\cdot2^{16})=25400000/473628672$. +@^sp@> + +The |mag| parameter is what \TeX\ calls \.{\\mag}, i.e., 1000 times the +desired magnification. The actual fraction by which dimensions are +multiplied is therefore $|mag|\cdot|num|/1000|den|$. Note that if a \TeX\ +source document does not call for any `\.{true}' dimensions, and if you +change it only by specifying a different \.{\\mag} setting, the \.{DVI} +file that \TeX\ creates will be completely unchanged except for the value +of |mag| in the preamble and postamble. (Fancy \.{DVI}-reading programs allow +users to override the |mag|~setting when a \.{DVI} file is being printed.) + +Finally, |k| and |x| allow the \.{DVI} writer to include a comment, which is not +interpreted further. The length of comment |x| is |k|, where |0 <= k < 256|. + +@d id_byte 2 /*identifies the kind of \.{DVI} files described here*/ + +@ Font definitions for a given font number |k| contain further parameters +$$\hbox{|c[4]| |s[4]| |d[4]| |a[1]| |l[1]| |n[a+l]|.}$$ +The four-byte value |c| is the check sum that \TeX\ found in the \.{TFM} +file for this font; |c| should match the check sum of the font found by +programs that read this \.{DVI} file. +@^check sum@> + +Parameter |s| contains a fixed-point scale factor that is applied to +the character widths in font |k|; font dimensions in \.{TFM} files and +other font files are relative to this quantity, which is called the +``at size'' elsewhere in this documentation. The value of |s| is +always positive and less than $2^{27}$. It is given in the same units +as the other \.{DVI} dimensions, i.e., in sp when \TeX82 has made the +file. Parameter |d| is similar to |s|; it is the ``design size,'' and +(like~|s|) it is given in \.{DVI} units. Thus, font |k| is to be used +at $|mag|\cdot s/1000d$ times its normal size. + +The remaining part of a font definition gives the external name of the font, +which is an ASCII string of length |a+l|. The number |a| is the length +of the ``area'' or directory, and |l| is the length of the font name itself; +the standard local system font area is supposed to be used when |a==0|. +The |n| field contains the area in its first |a| bytes. + +Font definitions must appear before the first use of a particular font number. +Once font |k| is defined, it must not be defined again; however, we +shall see below that font definitions appear in the postamble as well as +in the pages, so in this sense each font number is defined exactly twice, +if at all. Like |nop| commands, font definitions can +appear before the first |bop|, or between an |eop| and a |bop|. + +@ Sometimes it is desirable to make horizontal or vertical rules line up +precisely with certain features in characters of a font. It is possible to +guarantee the correct matching between \.{DVI} output and the characters +generated by \MF\ by adhering to the following principles: (1)~The \MF\ +characters should be positioned so that a bottom edge or left edge that is +supposed to line up with the bottom or left edge of a rule appears at the +reference point, i.e., in row~0 and column~0 of the \MF\ raster. This +ensures that the position of the rule will not be rounded differently when +the pixel size is not a perfect multiple of the units of measurement in +the \.{DVI} file. (2)~A typeset rule of height $a>0$ and width $b>0$ +should be equivalent to a \MF-generated character having black pixels in +precisely those raster positions whose \MF\ coordinates satisfy +|0 <= x < @t$\alpha$@>b| and |0 <= y < @t$\alpha$@>a|, where $\alpha$ is the number +of pixels per \.{DVI} unit. +@:METAFONT}{\MF@> +@^alignment of rules with characters@> +@^rules aligning with characters@> + +@ The last page in a \.{DVI} file is followed by `|post|'; this command +introduces the postamble, which summarizes important facts that \TeX\ has +accumulated about the file, making it possible to print subsets of the data +with reasonable efficiency. The postamble has the form +$$\vbox{\halign{\hbox{#\hfil}\cr + |post| |p[4]| |num[4]| |den[4]| |mag[4]| |l[4]| |u[4]| |s[2]| |t[2]|\cr + $\langle\,$font definitions$\,\rangle$\cr + |post_post| |q[4]| |i[1]| 223's$[{\G}4]$\cr}}$$ +Here |p| is a pointer to the final |bop| in the file. The next three +parameters, |num|, |den|, and |mag|, are duplicates of the quantities that +appeared in the preamble. + +Parameters |l| and |u| give respectively the height-plus-depth of the tallest +page and the width of the widest page, in the same units as other dimensions +of the file. These numbers might be used by a \.{DVI}-reading program to +position individual ``pages'' on large sheets of film or paper; however, +the standard convention for output on normal size paper is to position each +page so that the upper left-hand corner is exactly one inch from the left +and the top. Experience has shown that it is unwise to design \.{DVI}-to-printer +software that attempts cleverly to center the output; a fixed position of +the upper left corner is easiest for users to understand and to work with. +Therefore |l| and~|u| are often ignored. + +Parameter |s| is the maximum stack depth (i.e., the largest excess of +|push| commands over |pop| commands) needed to process this file. Then +comes |t|, the total number of pages (|bop| commands) present. + +The postamble continues with font definitions, which are any number of +\\{fnt\_def} commands as described above, possibly interspersed with |nop| +commands. Each font number that is used in the \.{DVI} file must be defined +exactly twice: Once before it is first selected by a \\{fnt} command, and once +in the postamble. + +@ The last part of the postamble, following the |post_post| byte that +signifies the end of the font definitions, contains |q|, a pointer to the +|post| command that started the postamble. An identification byte, |i|, +comes next; this currently equals~2, as in the preamble. + +The |i| byte is followed by four or more bytes that are all equal to +the decimal number 223 (i.e., 0337 in octal). \TeX\ puts out four to seven of +these trailing bytes, until the total length of the file is a multiple of +four bytes, since this works out best on machines that pack four bytes per +word; but any number of 223's is allowed, as long as there are at least four +of them. In effect, 223 is a sort of signature that is added at the very end. +@^Fuchs, David Raymond@> + +This curious way to finish off a \.{DVI} file makes it feasible for +\.{DVI}-reading programs to find the postamble first, on most computers, +even though \TeX\ wants to write the postamble last. Most operating +systems permit random access to individual words or bytes of a file, so +the \.{DVI} reader can start at the end and skip backwards over the 223's +until finding the identification byte. Then it can back up four bytes, read +|q|, and move to byte |q| of the file. This byte should, of course, +contain the value 248 (|post|); now the postamble can be read, so the +\.{DVI} reader can discover all the information needed for typesetting the +pages. Note that it is also possible to skip through the \.{DVI} file at +reasonably high speed to locate a particular page, if that proves +desirable. This saves a lot of time, since \.{DVI} files used in production +jobs tend to be large. + +Unfortunately, however, standard \PASCAL\ does not include the ability to +@^system dependencies@> +access a random position in a file, or even to determine the length of a file. +Almost all systems nowadays provide the necessary capabilities, so \.{DVI} +format has been designed to work most efficiently with modern operating systems. +But if \.{DVI} files have to be processed under the restrictions of standard +\PASCAL, one can simply read them from front to back, since the necessary +header information is present in the preamble and in the font definitions. +(The |l| and |u| and |s| and |t| parameters, which appear only in the +postamble, are ``frills'' that are handy but not absolutely necessary.) + +@* Shipping pages out. +After considering \TeX's eyes and stomach, we come now to the bowels. +@^bowels@> + +The |ship_out| procedure is given a pointer to a box; its mission is +to describe that box in \.{DVI} form, outputting a ``page'' to |dvi_file|. +The \.{DVI} coordinates $(h,v)=(0,0)$ should correspond to the upper left +corner of the box being shipped. + +Since boxes can be inside of boxes inside of boxes, the main work of +|ship_out| is done by two mutually recursive routines, |hlist_out| +and |vlist_out|, which traverse the hlists and vlists inside of horizontal +and vertical boxes. + +As individual pages are being processed, we need to accumulate +information about the entire set of pages, since such statistics must be +reported in the postamble. The global variables |total_pages|, |max_v|, +|max_h|, |max_push|, and |last_bop| are used to record this information. + +The variable |doing_leaders| is |true| while leaders are being output. +The variable |dead_cycles| contains the number of times an output routine +has been initiated since the last |ship_out|. + +A few additional global variables are also defined here for use in +|vlist_out| and |hlist_out|. They could have been local variables, but +that would waste stack space when boxes are deeply nested, since the +values of these variables are not needed during recursive calls. +@^recursion@> + +@<Glob...@>= +static int @!total_pages; /*the number of pages that have been shipped out*/ +static scaled @!max_v; /*maximum height-plus-depth of pages shipped so far*/ +static scaled @!max_h; /*maximum width of pages shipped so far*/ +static int @!max_push; /*deepest nesting of |push| commands encountered so far*/ +static int @!last_bop; /*location of previous |bop| in the \.{DVI} output*/ +static int @!dead_cycles; /*recent outputs that didn't ship anything out*/ +static bool @!doing_leaders; /*are we inside a leader box?*/ +@# +static quarterword @!c, @!f; /*character and font in current |char_node|*/ +static scaled @!rule_ht, @!rule_dp, @!rule_wd; /*size of current rule being output*/ +static pointer @!g; /*current glue specification*/ +static int @!lq, @!lr; /*quantities used in calculations for leaders*/ + +@ @<Set init...@>= +total_pages=0;max_v=0;max_h=0;max_push=0;last_bop=-1; +doing_leaders=false;dead_cycles=0;cur_s=-1; + +@ The \.{DVI} bytes are output to a buffer instead of being written directly +to the output file. This makes it possible to reduce the overhead of +subroutine calls, thereby measurably speeding up the computation, since +output of \.{DVI} bytes is part of \TeX's inner loop. And it has another +advantage as well, since we can change instructions in the buffer in order to +make the output more compact. For example, a `|down2|' command can be +changed to a `|y2|', thereby making a subsequent `|y0|' command possible, +saving two bytes. + +The output buffer is divided into two parts of equal size; the bytes found +in |dvi_buf[0 dotdot half_buf-1]| constitute the first half, and those in +|dvi_buf[half_buf dotdot dvi_buf_size-1]| constitute the second. The global +variable |dvi_ptr| points to the position that will receive the next +output byte. When |dvi_ptr| reaches |dvi_limit|, which is always equal +to one of the two values |half_buf| or |dvi_buf_size|, the half buffer that +is about to be invaded next is sent to the output and |dvi_limit| is +changed to its other value. Thus, there is always at least a half buffer's +worth of information present, except at the very beginning of the job. + +Bytes of the \.{DVI} file are numbered sequentially starting with 0; +the next byte to be generated will be number |dvi_offset+dvi_ptr|. +A byte is present in the buffer only if its number is | >= dvi_gone|. + +@<Types...@>= +typedef int16_t dvi_index; /*an index into the output buffer*/ + +@ Some systems may find it more efficient to make |dvi_buf| a || +array, since output of four bytes at once may be facilitated. +@^system dependencies@> + +@<Glob...@>= +static eight_bits @!dvi_buf[dvi_buf_size+1]; /*buffer for \.{DVI} output*/ +static dvi_index @!half_buf; /*half of |dvi_buf_size|*/ +static dvi_index @!dvi_limit; /*end of the current half buffer*/ +static dvi_index @!dvi_ptr; /*the next available buffer address*/ +static int @!dvi_offset; /*|dvi_buf_size| times the number of times the + output buffer has been fully emptied*/ +static int @!dvi_gone; /*the number of bytes already output to |dvi_file|*/ + +@ Initially the buffer is all in one piece; we will output half of it only +after it first fills up. + +@<Set init...@>= +half_buf=dvi_buf_size/2;dvi_limit=dvi_buf_size;dvi_ptr=0; +dvi_offset=0;dvi_gone=0; + +@ The actual output of |dvi_buf[a dotdot b]| to |dvi_file| is performed by calling +|write_dvi(a, b)|. For best results, this procedure should be optimized to +run as fast as possible on each particular system, since it is part of +\TeX's inner loop. It is safe to assume that |a| and |b+1| will both be +multiples of 4 when |write_dvi(a, b)| is called; therefore it is possible on +many machines to use efficient methods to pack four bytes per word and to +output an array of words with one system call. +@^system dependencies@> +@^inner loop@> +@^defecation@> + +@p static void write_dvi(dvi_index @!a, dvi_index @!b) +{@+int k; +for (k=a; k<=b; k++) pascal_write(dvi_file, "%c", dvi_buf[k]); +} + +@ To put a byte in the buffer without paying the cost of invoking a procedure +each time, we use the macro |dvi_out|. + +@d dvi_out(A) @+{@+dvi_buf[dvi_ptr]=A;incr(dvi_ptr); + if (dvi_ptr==dvi_limit) dvi_swap(); + } + +@p static void dvi_swap(void) /*outputs half of the buffer*/ +{@+if (dvi_limit==dvi_buf_size) + {@+write_dvi(0, half_buf-1);dvi_limit=half_buf; + dvi_offset=dvi_offset+dvi_buf_size;dvi_ptr=0; + } +else{@+write_dvi(half_buf, dvi_buf_size-1);dvi_limit=dvi_buf_size; + } +dvi_gone=dvi_gone+half_buf; +} + +@ Here is how we clean out the buffer when \TeX\ is all through; |dvi_ptr| +will be a multiple of~4. + +@<Empty the last bytes out of |dvi_buf|@>= +if (dvi_limit==half_buf) write_dvi(half_buf, dvi_buf_size-1); +if (dvi_ptr > 0) write_dvi(0, dvi_ptr-1) + +@ The |dvi_four| procedure outputs four bytes in two's complement notation, +without risking arithmetic overflow. + +@p static void dvi_four(int @!x) +{@+if (x >= 0) dvi_out(x/0100000000)@; +else{@+x=x+010000000000; + x=x+010000000000; + dvi_out((x/0100000000)+128); + } +x=x%0100000000;dvi_out(x/0200000); +x=x%0200000;dvi_out(x/0400); +dvi_out(x%0400); +} + +@ A mild optimization of the output is performed by the |dvi_pop| +routine, which issues a |pop| unless it is possible to cancel a +`|push| |pop|' pair. The parameter to |dvi_pop| is the byte address +following the old |push| that matches the new |pop|. + +@p static void dvi_pop(int @!l) +{@+if ((l==dvi_offset+dvi_ptr)&&(dvi_ptr > 0)) decr(dvi_ptr); +else dvi_out(pop); +} + +@ Here's a procedure that outputs a font definition. Since \TeX82 uses at +most 256 different fonts per job, |fnt_def1| is always used as the command code. + +@p static void dvi_font_def(internal_font_number @!f) +{@+int k; /*index into |str_pool|*/ +dvi_out(fnt_def1); +dvi_out(f-font_base-1);@/ +dvi_out(qo(font_check[f].b0)); +dvi_out(qo(font_check[f].b1)); +dvi_out(qo(font_check[f].b2)); +dvi_out(qo(font_check[f].b3));@/ +dvi_four(font_size[f]); +dvi_four(font_dsize[f]);@/ +dvi_out(length(font_area[f])); +dvi_out(length(font_name[f])); +@<Output the font name whose internal number is |f|@>; +} + +@ @<Output the font name whose internal number is |f|@>= +for (k=str_start[font_area[f]]; k<=str_start[font_area[f]+1]-1; k++) + dvi_out(so(str_pool[k])); +for (k=str_start[font_name[f]]; k<=str_start[font_name[f]+1]-1; k++) + dvi_out(so(str_pool[k])) + +@ Versions of \TeX\ intended for small computers might well choose to omit +the ideas in the next few parts of this program, since it is not really +necessary to optimize the \.{DVI} code by making use of the |w0|, |x0|, +|y0|, and |z0| commands. Furthermore, the algorithm that we are about to +describe does not pretend to give an optimum reduction in the length +of the \.{DVI} code; after all, speed is more important than compactness. +But the method is surprisingly effective, and it takes comparatively little +time. + +We can best understand the basic idea by first considering a simpler problem +that has the same essential characteristics. Given a sequence of digits, +say $3\,1\,4\,1\,5\,9\,2\,6\,5\,3\,5\,8\,9$, we want to assign subscripts +$d$, $y$, or $z$ to each digit so as to maximize the number of ``$y$-hits'' +and ``$z$-hits''; a $y$-hit is an instance of two appearances of the same +digit with the subscript $y$, where no $y$'s intervene between the two +appearances, and a $z$-hit is defined similarly. For example, the sequence +above could be decorated with subscripts as follows: +$$3_z\,1_y\,4_d\,1_y\,5_y\,9_d\,2_d\,6_d\,5_y\,3_z\,5_y\,8_d\,9_d.$$ +There are three $y$-hits ($1_y\ldots1_y$ and $5_y\ldots5_y\ldots5_y$) and +one $z$-hit ($3_z\ldots3_z$); there are no $d$-hits, since the two appearances +of $9_d$ have $d$'s between them, but we don't count $d$-hits so it doesn't +matter how many there are. These subscripts are analogous to the \.{DVI} +commands called \\{down}, $y$, and $z$, and the digits are analogous to +different amounts of vertical motion; a $y$-hit or $z$-hit corresponds to +the opportunity to use the one-byte commands |y0| or |z0| in a \.{DVI} file. + +\TeX's method of assigning subscripts works like this: Append a new digit, +say $\delta$, to the right of the sequence. Now look back through the +sequence until one of the following things happens: (a)~You see +$\delta_y$ or $\delta_z$, and this was the first time you encountered a +$y$ or $z$ subscript, respectively. Then assign $y$ or $z$ to the new +$\delta$; you have scored a hit. (b)~You see $\delta_d$, and no $y$ +subscripts have been encountered so far during this search. Then change +the previous $\delta_d$ to $\delta_y$ (this corresponds to changing a +command in the output buffer), and assign $y$ to the new $\delta$; it's +another hit. (c)~You see $\delta_d$, and a $y$ subscript has been seen +but not a $z$. Change the previous $\delta_d$ to $\delta_z$ and assign +$z$ to the new $\delta$. (d)~You encounter both $y$ and $z$ subscripts +before encountering a suitable $\delta$, or you scan all the way to the +front of the sequence. Assign $d$ to the new $\delta$; this assignment may +be changed later. + +The subscripts $3_z\,1_y\,4_d\ldots\,$ in the example above were, in fact, +produced by this procedure, as the reader can verify. (Go ahead and try it.) + +@ In order to implement such an idea, \TeX\ maintains a stack of pointers +to the \\{down}, $y$, and $z$ commands that have been generated for the +current page. And there is a similar stack for \\{right}, |w|, and |x| +commands. These stacks are called the down stack and right stack, and their +top elements are maintained in the variables |down_ptr| and |right_ptr|. + +Each entry in these stacks contains four fields: The |width| field is +the amount of motion down or to the right; the |location| field is the +byte number of the \.{DVI} command in question (including the appropriate +|dvi_offset|); the |link| field points to the next item below this one +on the stack; and the |info| field encodes the options for possible change +in the \.{DVI} command. + +@d movement_node_size 3 /*number of words per entry in the down and right stacks*/ +@d location(A) mem[A+2].i /*\.{DVI} byte number for a movement command*/ + +@<Glob...@>= +static pointer @!down_ptr, @!right_ptr; /*heads of the down and right stacks*/ + +@ @<Set init...@>= +down_ptr=null;right_ptr=null; + +@ Here is a subroutine that produces a \.{DVI} command for some specified +downward or rightward motion. It has two parameters: |w| is the amount +of motion, and |o| is either |down1| or |right1|. We use the fact that +the command codes have convenient arithmetic properties: |y1-down1==w1-right1| +and |z1-down1==x1-right1|. + +@p static void movement(scaled @!w, eight_bits @!o) +{@+ +small_number mstate; /*have we seen a |y| or |z|?*/ +pointer @!p, @!q; /*current and top nodes on the stack*/ +int @!k; /*index into |dvi_buf|, modulo |dvi_buf_size|*/ +q=get_node(movement_node_size); /*new node for the top of the stack*/ +width(q)=w;location(q)=dvi_offset+dvi_ptr; +if (o==down1) + {@+link(q)=down_ptr;down_ptr=q; + } +else{@+link(q)=right_ptr;right_ptr=q; + } +@<Look at the other stack entries until deciding what sort of \.{DVI} command +to generate; |goto found| if node |p| is a ``hit''@>; +@<Generate a |down| or |right| command for |w| and |return|@>; +found: @<Generate a |y0| or |z0| command in order to reuse a previous appearance +of~|w|@>; +} + +@ The |info| fields in the entries of the down stack or the right stack +have six possible settings: |y_here| or |z_here| mean that the \.{DVI} +command refers to |y| or |z|, respectively (or to |w| or |x|, in the +case of horizontal motion); |yz_OK| means that the \.{DVI} command is +\\{down} (or \\{right}) but can be changed to either |y| or |z| (or +to either |w| or |x|); |y_OK| means that it is \\{down} and can be changed +to |y| but not |z|; |z_OK| is similar; and |d_fixed| means it must stay +\\{down}. + +The four settings |yz_OK|, |y_OK|, |z_OK|, |d_fixed| would not need to +be distinguished from each other if we were simply solving the +digit-subscripting problem mentioned above. But in \TeX's case there is +a complication because of the nested structure of |push| and |pop| +commands. Suppose we add parentheses to the digit-subscripting problem, +redefining hits so that $\delta_y\ldots \delta_y$ is a hit if all $y$'s between +the $\delta$'s are enclosed in properly nested parentheses, and if the +parenthesis level of the right-hand $\delta_y$ is deeper than or equal to +that of the left-hand one. Thus, `(' and `)' correspond to `|push|' +and `|pop|'. Now if we want to assign a subscript to the final 1 in the +sequence +$$2_y\,7_d\,1_d\,(\,8_z\,2_y\,8_z\,)\,1$$ +we cannot change the previous $1_d$ to $1_y$, since that would invalidate +the $2_y\ldots2_y$ hit. But we can change it to $1_z$, scoring a hit +since the intervening $8_z$'s are enclosed in parentheses. + +The program below removes movement nodes that are introduced after a |push|, +before it outputs the corresponding |pop|. + +@d y_here 1 /*|info| when the movement entry points to a |y| command*/ +@d z_here 2 /*|info| when the movement entry points to a |z| command*/ +@d yz_OK 3 /*|info| corresponding to an unconstrained \\{down} command*/ +@d y_OK 4 /*|info| corresponding to a \\{down} that can't become a |z|*/ +@d z_OK 5 /*|info| corresponding to a \\{down} that can't become a |y|*/ +@d d_fixed 6 /*|info| corresponding to a \\{down} that can't change*/ + +@ When the |movement| procedure gets to the label |found|, the value of +|info(p)| will be either |y_here| or |z_here|. If it is, say, |y_here|, +the procedure generates a |y0| command (or a |w0| command), and marks +all |info| fields between |q| and |p| so that |y| is not OK in that range. + +@<Generate a |y0| or |z0| command...@>= +info(q)=info(p); +if (info(q)==y_here) + {@+dvi_out(o+y0-down1); /*|y0| or |w0|*/ + while (link(q)!=p) + {@+q=link(q); + switch (info(q)) { + case yz_OK: info(q)=z_OK;@+break; + case y_OK: info(q)=d_fixed;@+break; + default:do_nothing; + } + } + } +else{@+dvi_out(o+z0-down1); /*|z0| or |x0|*/ + while (link(q)!=p) + {@+q=link(q); + switch (info(q)) { + case yz_OK: info(q)=y_OK;@+break; + case z_OK: info(q)=d_fixed;@+break; + default:do_nothing; + } + } + } + +@ @<Generate a |down| or |right|...@>= +info(q)=yz_OK; +if (abs(w) >= 040000000) + {@+dvi_out(o+3); /*|down4| or |right4|*/ + dvi_four(w);return; + } +if (abs(w) >= 0100000) + {@+dvi_out(o+2); /*|down3| or |right3|*/ + if (w < 0) w=w+0100000000; + dvi_out(w/0200000);w=w%0200000;goto label2; + } +if (abs(w) >= 0200) + {@+dvi_out(o+1); /*|down2| or |right2|*/ + if (w < 0) w=w+0200000; + goto label2; + } +dvi_out(o); /*|down1| or |right1|*/ +if (w < 0) w=w+0400; +goto label1; +label2: dvi_out(w/0400); +label1: dvi_out(w%0400);return + +@ As we search through the stack, we are in one of three states, +|y_seen|, |z_seen|, or |none_seen|, depending on whether we have +encountered |y_here| or |z_here| nodes. These states are encoded as +multiples of 6, so that they can be added to the |info| fields for quick +decision-making. +@^inner loop@> + +@d none_seen 0 /*no |y_here| or |z_here| nodes have been encountered yet*/ +@d y_seen 6 /*we have seen |y_here| but not |z_here|*/ +@d z_seen 12 /*we have seen |z_here| but not |y_here|*/ + +@<Look at the other stack entries until deciding...@>= +p=link(q);mstate=none_seen; +while (p!=null) + {@+if (width(p)==w) @<Consider a node with matching width; |goto found| +if it's a hit@>@; + else switch (mstate+info(p)) { + case none_seen+y_here: mstate=y_seen;@+break; + case none_seen+z_here: mstate=z_seen;@+break; + case y_seen+z_here: case z_seen+y_here: goto not_found; + default:do_nothing; + } + p=link(p); + } +not_found: + +@ We might find a valid hit in a |y| or |z| byte that is already gone +from the buffer. But we can't change bytes that are gone forever; ``the +moving finger writes, $\ldots\,\,$.'' + +@<Consider a node with matching width...@>= +switch (mstate+info(p)) { +case none_seen+yz_OK: case none_seen+y_OK: + case z_seen+yz_OK: case z_seen+y_OK: @t@>@;@/ + if (location(p) < dvi_gone) goto not_found; + else@<Change buffered instruction to |y| or |w| and |goto found|@>@;@+break; +case none_seen+z_OK: case y_seen+yz_OK: + case y_seen+z_OK: @t@>@;@/ + if (location(p) < dvi_gone) goto not_found; + else@<Change buffered instruction to |z| or |x| and |goto found|@>@;@+break; +case none_seen+y_here: case none_seen+z_here: + case y_seen+z_here: case z_seen+y_here: goto found; +default:do_nothing; +} + +@ @<Change buffered instruction to |y| or |w| and |goto found|@>= +{@+k=location(p)-dvi_offset; +if (k < 0) k=k+dvi_buf_size; +dvi_buf[k]=dvi_buf[k]+y1-down1; +info(p)=y_here;goto found; +} + +@ @<Change buffered instruction to |z| or |x| and |goto found|@>= +{@+k=location(p)-dvi_offset; +if (k < 0) k=k+dvi_buf_size; +dvi_buf[k]=dvi_buf[k]+z1-down1; +info(p)=z_here;goto found; +} + +@ In case you are wondering when all the movement nodes are removed from +\TeX's memory, the answer is that they are recycled just before +|hlist_out| and |vlist_out| finish outputting a box. This restores the +down and right stacks to the state they were in before the box was output, +except that some |info|'s may have become more restrictive. + +@p static void prune_movements(int @!l) + /*delete movement nodes with |location >= l|*/ +{@+ +pointer p; /*node being deleted*/ +while (down_ptr!=null) + {@+if (location(down_ptr) < l) goto done; + p=down_ptr;down_ptr=link(p);free_node(p, movement_node_size); + } +done: while (right_ptr!=null) + {@+if (location(right_ptr) < l) return; + p=right_ptr;right_ptr=link(p);free_node(p, movement_node_size); + } +} + +@ The actual distances by which we want to move might be computed as the +sum of several separate movements. For example, there might be several +glue nodes in succession, or we might want to move right by the width of +some box plus some amount of glue. More importantly, the baselineskip +distances are computed in terms of glue together with the depth and +height of adjacent boxes, and we want the \.{DVI} file to lump these +three quantities together into a single motion. + +Therefore, \TeX\ maintains two pairs of global variables: |dvi_h| and |dvi_v| +are the |h| and |v| coordinates corresponding to the commands actually +output to the \.{DVI} file, while |cur_h| and |cur_v| are the coordinates +corresponding to the current state of the output routines. Coordinate +changes will accumulate in |cur_h| and |cur_v| without being reflected +in the output, until such a change becomes necessary or desirable; we +can call the |movement| procedure whenever we want to make |dvi_h==cur_h| +or |dvi_v==cur_v|. + +The current font reflected in the \.{DVI} output is called |dvi_f|; +there is no need for a `\\{cur\_f}' variable. + +The depth of nesting of |hlist_out| and |vlist_out| is called |cur_s|; +this is essentially the depth of |push| commands in the \.{DVI} output. + +@d synch_h if (cur_h!=dvi_h) + {@+movement(cur_h-dvi_h, right1);dvi_h=cur_h; + } +@d synch_v if (cur_v!=dvi_v) + {@+movement(cur_v-dvi_v, down1);dvi_v=cur_v; + } + +@<Glob...@>= +static scaled @!dvi_h, @!dvi_v; /*a \.{DVI} reader program thinks we are here*/ +static scaled @!cur_h, @!cur_v; /*\TeX\ thinks we are here*/ +static internal_font_number @!dvi_f; /*the current font*/ +static int @!cur_s; /*current depth of output box nesting, initially $-1$*/ + +@ @<Initialize variables as |ship_out| begins@>= +dvi_h=0;dvi_v=0;cur_h=h_offset;dvi_f=null_font; +ensure_dvi_open; +if (total_pages==0) + {@+dvi_out(pre);dvi_out(id_byte); /*output the preamble*/ +@^preamble of \.{DVI} file@> + dvi_four(25400000);dvi_four(473628672); /*conversion ratio for sp*/ + prepare_mag();dvi_four(mag); /*magnification factor is frozen*/ + old_setting=selector;selector=new_string; + print(" TeX output ");print_int(year);print_char('.'); + print_two(month);print_char('.');print_two(day); + print_char(':');print_two(time/60); + print_two(time%60); + selector=old_setting;dvi_out(cur_length); + for (s=str_start[str_ptr]; s<=pool_ptr-1; s++) dvi_out(so(str_pool[s])); + pool_ptr=str_start[str_ptr]; /*flush the current string*/ + } + +@ When |hlist_out| is called, its duty is to output the box represented +by the |hlist_node| pointed to by |temp_ptr|. The reference point of that +box has coordinates |(cur_h, cur_v)|. + +Similarly, when |vlist_out| is called, its duty is to output the box represented +by the |vlist_node| pointed to by |temp_ptr|. The reference point of that +box has coordinates |(cur_h, cur_v)|. +@^recursion@> + +@p static void vlist_out(void); /*|hlist_out| and |vlist_out| are mutually + recursive*/ + +@ The recursive procedures |hlist_out| and |vlist_out| each have local variables +|save_h| and |save_v| to hold the values of |dvi_h| and |dvi_v| just before +entering a new level of recursion. In effect, the values of |save_h| and +|save_v| on \TeX's run-time stack correspond to the values of |h| and |v| +that a \.{DVI}-reading program will push onto its coordinate stack. + +@p @t\4@>@<Declare procedures needed in |hlist_out|, |vlist_out|@>@t@>@/ +static void hlist_out(void) /*output an |hlist_node| box*/ +{@+ +scaled base_line; /*the baseline coordinate for this box*/ +scaled @!left_edge; /*the left coordinate for this box*/ +scaled @!save_h, @!save_v; /*what |dvi_h| and |dvi_v| should pop to*/ +pointer @!this_box; /*pointer to containing box*/ +glue_ord @!g_order; /*applicable order of infinity for glue*/ +int @!g_sign; /*selects type of glue*/ +pointer @!p; /*current position in the hlist*/ +int @!save_loc; /*\.{DVI} byte location upon entry*/ +pointer @!leader_box; /*the leader box being replicated*/ +scaled @!leader_wd; /*width of leader box being replicated*/ +scaled @!lx; /*extra space between leader boxes*/ +bool @!outer_doing_leaders; /*were we doing leaders?*/ +scaled @!edge; /*left edge of sub-box, or right edge of leader space*/ +double @!glue_temp; /*glue value before rounding*/ +double @!cur_glue; /*glue seen so far*/ +scaled @!cur_g; /*rounded equivalent of |cur_glue| times the glue ratio*/ +cur_g=0;cur_glue=float_constant(0); +this_box=temp_ptr;g_order=glue_order(this_box); +g_sign=glue_sign(this_box);p=list_ptr(this_box); +incr(cur_s); +if (cur_s > 0) dvi_out(push); +if (cur_s > max_push) max_push=cur_s; +save_loc=dvi_offset+dvi_ptr;base_line=cur_v;left_edge=cur_h; +while (p!=null) @<Output node |p| for |hlist_out| and move to the next node, +maintaining the condition |cur_v=base_line|@>; +prune_movements(save_loc); +if (cur_s > 0) dvi_pop(save_loc); +decr(cur_s); +} + +@ We ought to give special care to the efficiency of one part of |hlist_out|, +since it belongs to \TeX's inner loop. When a |char_node| is encountered, +we save a little time by processing several nodes in succession until +reaching a non-|char_node|. The program uses the fact that |set_char_0==0|. +@^inner loop@> + +@<Output node |p| for |hlist_out|...@>= +reswitch: if (is_char_node(p)) + {@+synch_h;synch_v; + @/do@+{f=font(p);c=character(p); + if (f!=dvi_f) @<Change font |dvi_f| to |f|@>; + if (c >= qi(128)) dvi_out(set1); + dvi_out(qo(c));@/ + cur_h=cur_h+char_width(f, char_info(f, c)); + p=link(p); + }@+ while (!(!is_char_node(p))); + dvi_h=cur_h; + } +else@<Output the non-|char_node| |p| for |hlist_out| and move to the next +node@>@; + +@ @<Change font |dvi_f| to |f|@>= +{@+if (!font_used[f]) + {@+dvi_font_def(f);font_used[f]=true; + } +if (f <= 64+font_base) dvi_out(f-font_base-1+fnt_num_0)@; +else{@+dvi_out(fnt1);dvi_out(f-font_base-1); + } +dvi_f=f; +} + +@ @<Output the non-|char_node| |p| for |hlist_out|...@>= +{@+switch (type(p)) { +case hlist_node: case vlist_node: @<Output a box in an hlist@>@;@+break; +case rule_node: {@+rule_ht=height(p);rule_dp=depth(p);rule_wd=width(p); + goto fin_rule; + } +case whatsit_node: @<Output the whatsit node |p| in an hlist@>;@+break; +case glue_node: @<Move right or output leaders@>@; +case kern_node: case math_node: cur_h=cur_h+width(p);@+break; +case ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>@; +default:do_nothing; +} @/ +goto next_p; +fin_rule: @<Output a rule in an hlist@>; +move_past: cur_h=cur_h+rule_wd; +next_p: p=link(p); +} + +@ @<Output a box in an hlist@>= +if (list_ptr(p)==null) cur_h=cur_h+width(p); +else{@+save_h=dvi_h;save_v=dvi_v; + cur_v=base_line+shift_amount(p); /*shift the box down*/ + temp_ptr=p;edge=cur_h; + if (type(p)==vlist_node) vlist_out();@+else hlist_out(); + dvi_h=save_h;dvi_v=save_v; + cur_h=edge+width(p);cur_v=base_line; + } + +@ @<Output a rule in an hlist@>= +if (is_running(rule_ht)) rule_ht=height(this_box); +if (is_running(rule_dp)) rule_dp=depth(this_box); +rule_ht=rule_ht+rule_dp; /*this is the rule thickness*/ +if ((rule_ht > 0)&&(rule_wd > 0)) /*we don't output empty rules*/ + {@+synch_h;cur_v=base_line+rule_dp;synch_v; + dvi_out(set_rule);dvi_four(rule_ht);dvi_four(rule_wd); + cur_v=base_line;dvi_h=dvi_h+rule_wd; + } + +@ @d billion float_constant(1000000000) +@d vet_glue(A) glue_temp=A; + if (glue_temp > billion) + glue_temp=billion; + else if (glue_temp < -billion) + glue_temp=-billion + +@<Move right or output leaders@>= +{@+g=glue_ptr(p);rule_wd=width(g)-cur_g; +if (g_sign!=normal) + {@+if (g_sign==stretching) + {@+if (stretch_order(g)==g_order) + {@+cur_glue=cur_glue+stretch(g); + vet_glue(unfix(glue_set(this_box))*cur_glue); +@^real multiplication@> + cur_g=round(glue_temp); + } + } + else if (shrink_order(g)==g_order) + {@+cur_glue=cur_glue-shrink(g); + vet_glue(unfix(glue_set(this_box))*cur_glue); + cur_g=round(glue_temp); + } + } +rule_wd=rule_wd+cur_g; +if (subtype(p) >= a_leaders) + @<Output leaders in an hlist, |goto fin_rule| if a rule or to |next_p| if +done@>; +goto move_past; +} + +@ @<Output leaders in an hlist...@>= +{@+leader_box=leader_ptr(p); +if (type(leader_box)==rule_node) + {@+rule_ht=height(leader_box);rule_dp=depth(leader_box); + goto fin_rule; + } +leader_wd=width(leader_box); +if ((leader_wd > 0)&&(rule_wd > 0)) + {@+rule_wd=rule_wd+10; /*compensate for floating-point rounding*/ + edge=cur_h+rule_wd;lx=0; + @<Let |cur_h| be the position of the first box, and set |leader_wd+lx| to +the spacing between corresponding parts of boxes@>; + while (cur_h+leader_wd <= edge) + @<Output a leader box at |cur_h|, then advance |cur_h| by |leader_wd+lx|@>; + cur_h=edge-10;goto next_p; + } +} + +@ The calculations related to leaders require a bit of care. First, in the +case of |a_leaders| (aligned leaders), we want to move |cur_h| to +|left_edge| plus the smallest multiple of |leader_wd| for which the result +is not less than the current value of |cur_h|; i.e., |cur_h| should become +$|left_edge|+|leader_wd|\times\lceil +(|cur_h|-|left_edge|)/|leader_wd|\rceil$. The program here should work in +all cases even though some implementations of \PASCAL\ give nonstandard +results for the |/| operation when |cur_h| is less than |left_edge|. + +In the case of |c_leaders| (centered leaders), we want to increase |cur_h| +by half of the excess space not occupied by the leaders; and in the +case of |x_leaders| (expanded leaders) we increase |cur_h| +by $1/(q+1)$ of this excess space, where $q$ is the number of times the +leader box will be replicated. Slight inaccuracies in the division might +accumulate; half of this rounding error is placed at each end of the leaders. + +@<Let |cur_h| be the position of the first box,...@>= +if (subtype(p)==a_leaders) + {@+save_h=cur_h; + cur_h=left_edge+leader_wd*((cur_h-left_edge)/leader_wd); + if (cur_h < save_h) cur_h=cur_h+leader_wd; + } +else{@+lq=rule_wd/leader_wd; /*the number of box copies*/ + lr=rule_wd%leader_wd; /*the remaining space*/ + if (subtype(p)==c_leaders) cur_h=cur_h+(lr/2); + else{@+lx=lr/(lq+1); + cur_h=cur_h+((lr-(lq-1)*lx)/2); + } + } + +@ The `\\{synch}' operations here are intended to decrease the number of +bytes needed to specify horizontal and vertical motion in the \.{DVI} output. + +@<Output a leader box at |cur_h|,...@>= +{@+cur_v=base_line+shift_amount(leader_box);synch_v;save_v=dvi_v;@/ +synch_h;save_h=dvi_h;temp_ptr=leader_box; +outer_doing_leaders=doing_leaders;doing_leaders=true; +if (type(leader_box)==vlist_node) vlist_out();@+else hlist_out(); +doing_leaders=outer_doing_leaders; +dvi_v=save_v;dvi_h=save_h;cur_v=base_line; +cur_h=save_h+leader_wd+lx; +} + +@ The |vlist_out| routine is similar to |hlist_out|, but a bit simpler. + +@p static void vlist_out(void) /*output a |vlist_node| box*/ +{@+ +scaled left_edge; /*the left coordinate for this box*/ +scaled @!top_edge; /*the top coordinate for this box*/ +scaled @!save_h, @!save_v; /*what |dvi_h| and |dvi_v| should pop to*/ +pointer @!this_box; /*pointer to containing box*/ +glue_ord @!g_order; /*applicable order of infinity for glue*/ +int @!g_sign; /*selects type of glue*/ +pointer @!p; /*current position in the vlist*/ +int @!save_loc; /*\.{DVI} byte location upon entry*/ +pointer @!leader_box; /*the leader box being replicated*/ +scaled @!leader_ht; /*height of leader box being replicated*/ +scaled @!lx; /*extra space between leader boxes*/ +bool @!outer_doing_leaders; /*were we doing leaders?*/ +scaled @!edge; /*bottom boundary of leader space*/ +double @!glue_temp; /*glue value before rounding*/ +double @!cur_glue; /*glue seen so far*/ +scaled @!cur_g; /*rounded equivalent of |cur_glue| times the glue ratio*/ +cur_g=0;cur_glue=float_constant(0); +this_box=temp_ptr;g_order=glue_order(this_box); +g_sign=glue_sign(this_box);p=list_ptr(this_box); +incr(cur_s); +if (cur_s > 0) dvi_out(push); +if (cur_s > max_push) max_push=cur_s; +save_loc=dvi_offset+dvi_ptr;left_edge=cur_h;cur_v=cur_v-height(this_box); +top_edge=cur_v; +while (p!=null) @<Output node |p| for |vlist_out| and move to the next node, +maintaining the condition |cur_h=left_edge|@>; +prune_movements(save_loc); +if (cur_s > 0) dvi_pop(save_loc); +decr(cur_s); +} + +@ @<Output node |p| for |vlist_out|...@>= +{@+if (is_char_node(p)) confusion("vlistout"); +@:this can't happen vlistout}{\quad vlistout@> +else@<Output the non-|char_node| |p| for |vlist_out|@>; +next_p: p=link(p); +} + +@ @<Output the non-|char_node| |p| for |vlist_out|@>= +{@+switch (type(p)) { +case hlist_node: case vlist_node: @<Output a box in a vlist@>@;@+break; +case rule_node: {@+rule_ht=height(p);rule_dp=depth(p);rule_wd=width(p); + goto fin_rule; + } +case whatsit_node: @<Output the whatsit node |p| in a vlist@>;@+break; +case glue_node: @<Move down or output leaders@>@; +case kern_node: cur_v=cur_v+width(p);@+break; +default:do_nothing; +} @/ +goto next_p; +fin_rule: @<Output a rule in a vlist, |goto next_p|@>; +move_past: cur_v=cur_v+rule_ht; +} + +@ The |synch_v| here allows the \.{DVI} output to use one-byte commands +for adjusting |v| in most cases, since the baselineskip distance will +usually be constant. + +@<Output a box in a vlist@>= +if (list_ptr(p)==null) cur_v=cur_v+height(p)+depth(p); +else{@+cur_v=cur_v+height(p);synch_v; + save_h=dvi_h;save_v=dvi_v; + cur_h=left_edge+shift_amount(p); /*shift the box right*/ + temp_ptr=p; + if (type(p)==vlist_node) vlist_out();@+else hlist_out(); + dvi_h=save_h;dvi_v=save_v; + cur_v=save_v+depth(p);cur_h=left_edge; + } + +@ @<Output a rule in a vlist...@>= +if (is_running(rule_wd)) rule_wd=width(this_box); +rule_ht=rule_ht+rule_dp; /*this is the rule thickness*/ +cur_v=cur_v+rule_ht; +if ((rule_ht > 0)&&(rule_wd > 0)) /*we don't output empty rules*/ + {@+synch_h;synch_v; + dvi_out(put_rule);dvi_four(rule_ht);dvi_four(rule_wd); + } +goto next_p + +@ @<Move down or output leaders@>= +{@+g=glue_ptr(p);rule_ht=width(g)-cur_g; +if (g_sign!=normal) + {@+if (g_sign==stretching) + {@+if (stretch_order(g)==g_order) + {@+cur_glue=cur_glue+stretch(g); + vet_glue(unfix(glue_set(this_box))*cur_glue); +@^real multiplication@> + cur_g=round(glue_temp); + } + } + else if (shrink_order(g)==g_order) + {@+cur_glue=cur_glue-shrink(g); + vet_glue(unfix(glue_set(this_box))*cur_glue); + cur_g=round(glue_temp); + } + } +rule_ht=rule_ht+cur_g; +if (subtype(p) >= a_leaders) + @<Output leaders in a vlist, |goto fin_rule| if a rule or to |next_p| if +done@>; +goto move_past; +} + +@ @<Output leaders in a vlist...@>= +{@+leader_box=leader_ptr(p); +if (type(leader_box)==rule_node) + {@+rule_wd=width(leader_box);rule_dp=0; + goto fin_rule; + } +leader_ht=height(leader_box)+depth(leader_box); +if ((leader_ht > 0)&&(rule_ht > 0)) + {@+rule_ht=rule_ht+10; /*compensate for floating-point rounding*/ + edge=cur_v+rule_ht;lx=0; + @<Let |cur_v| be the position of the first box, and set |leader_ht+lx| to +the spacing between corresponding parts of boxes@>; + while (cur_v+leader_ht <= edge) + @<Output a leader box at |cur_v|, then advance |cur_v| by |leader_ht+lx|@>; + cur_v=edge-10;goto next_p; + } +} + +@ @<Let |cur_v| be the position of the first box,...@>= +if (subtype(p)==a_leaders) + {@+save_v=cur_v; + cur_v=top_edge+leader_ht*((cur_v-top_edge)/leader_ht); + if (cur_v < save_v) cur_v=cur_v+leader_ht; + } +else{@+lq=rule_ht/leader_ht; /*the number of box copies*/ + lr=rule_ht%leader_ht; /*the remaining space*/ + if (subtype(p)==c_leaders) cur_v=cur_v+(lr/2); + else{@+lx=lr/(lq+1); + cur_v=cur_v+((lr-(lq-1)*lx)/2); + } + } + +@ When we reach this part of the program, |cur_v| indicates the top of a +leader box, not its baseline. + +@<Output a leader box at |cur_v|,...@>= +{@+cur_h=left_edge+shift_amount(leader_box);synch_h;save_h=dvi_h;@/ +cur_v=cur_v+height(leader_box);synch_v;save_v=dvi_v; +temp_ptr=leader_box; +outer_doing_leaders=doing_leaders;doing_leaders=true; +if (type(leader_box)==vlist_node) vlist_out();@+else hlist_out(); +doing_leaders=outer_doing_leaders; +dvi_v=save_v;dvi_h=save_h;cur_h=left_edge; +cur_v=save_v-height(leader_box)+leader_ht+lx; +} + +@ The |hlist_out| and |vlist_out| procedures are now complete, so we are +ready for the |ship_out| routine that gets them started in the first place. + +@p static void ship_out(pointer @!p) /*output the box |p|*/ +{@+ +int page_loc; /*location of the current |bop|*/ +int @!j, @!k; /*indices to first ten count registers*/ +int @!s; /*index into |str_pool|*/ +int @!old_setting; /*saved |selector| setting*/ +@<Local variables to save the profiling context@>@; +@<Charge the time used here on |ship_out|@>@; +if (tracing_output > 0) + {@+print_nl("");print_ln(); + print("Completed box being shipped out"); +@.Completed box...@> + } +if (term_offset > max_print_line-9) print_ln(); +else if ((term_offset > 0)||(file_offset > 0)) print_char(' '); +print_char('[');j=9; +while ((count(j)==0)&&(j > 0)) decr(j); +for (k=0; k<=j; k++) + {@+print_int(count(k)); + if (k < j) print_char('.'); + } +update_terminal; +if (tracing_output > 0) + {@+print_char(']'); + begin_diagnostic();show_box(p);end_diagnostic(true); + } +@<Ship box |p| out@>; +if (tracing_output <= 0) print_char(']'); +dead_cycles=0; +update_terminal; /*progress report*/ +@<Flush the box from memory, showing statistics if requested@>; +@<restore the previous current file, line, and command@>@; +} + +@ @<Flush the box from memory, showing statistics if requested@>= +#ifdef @!STAT +if (tracing_stats > 1) + {@+print_nl("Memory usage before: "); +@.Memory usage...@> + print_int(var_used);print_char('&'); + print_int(dyn_used);print_char(';'); + } +#endif +flush_node_list(p); +#ifdef @!STAT +if (tracing_stats > 1) + {@+print(" after: "); + print_int(var_used);print_char('&'); + print_int(dyn_used);print("; still untouched: "); + print_int(hi_mem_min-lo_mem_max-1);print_ln(); + } +#endif + +@ @<Ship box |p| out@>= +@<Update the values of |max_h| and |max_v|; but if the page is too large, +|goto done|@>; +@<Initialize variables as |ship_out| begins@>; +page_loc=dvi_offset+dvi_ptr; +dvi_out(bop); +for (k=0; k<=9; k++) dvi_four(count(k)); +dvi_four(last_bop);last_bop=page_loc; +cur_v=height(p)+v_offset;temp_ptr=p; +if (type(p)==vlist_node) vlist_out();@+else hlist_out(); +dvi_out(eop);incr(total_pages);cur_s=-1; +done: + +@ Sometimes the user will generate a huge page because other error messages +are being ignored. Such pages are not output to the \.{dvi} file, since they +may confuse the printing software. + +@<Update the values of |max_h| and |max_v|; but if the page is too large...@>= +if ((height(p) > max_dimen)||@|(depth(p) > max_dimen)||@| + (height(p)+depth(p)+v_offset > max_dimen)||@| + (width(p)+h_offset > max_dimen)) + {@+print_err("Huge page cannot be shipped out"); +@.Huge page...@> + help2("The page just created is more than 18 feet tall or",@/ + "more than 18 feet wide, so I suspect something went wrong."); + error(); + if (tracing_output <= 0) + {@+begin_diagnostic(); + print_nl("The following box has been deleted:"); +@.The following...deleted@> + show_box(p); + end_diagnostic(true); + } + goto done; + } +if (height(p)+depth(p)+v_offset > max_v) max_v=height(p)+depth(p)+v_offset; +if (width(p)+h_offset > max_h) max_h=width(p)+h_offset + +@ At the end of the program, we must finish things off by writing the +post\-amble. If |total_pages==0|, the \.{DVI} file was never opened. +If |total_pages >= 65536|, the \.{DVI} file will lie. And if +|max_push >= 65536|, the user deserves whatever chaos might ensue. + +An integer variable |k| will be declared for use by this routine. + +@<Finish the \.{DVI} file@>= +while (cur_s > -1) + {@+if (cur_s > 0) dvi_out(pop)@; + else{@+dvi_out(eop);incr(total_pages); + } + decr(cur_s); + } +if (total_pages==0) print_nl("No pages of output."); +@.No pages of output@> +else{@+dvi_out(post); /*beginning of the postamble*/ + dvi_four(last_bop);last_bop=dvi_offset+dvi_ptr-5; /*|post| location*/ + dvi_four(25400000);dvi_four(473628672); /*conversion ratio for sp*/ + prepare_mag();dvi_four(mag); /*magnification factor*/ + dvi_four(max_v);dvi_four(max_h);@/ + dvi_out(max_push/256);dvi_out(max_push%256);@/ + dvi_out((total_pages/256)%256);dvi_out(total_pages%256);@/ + @<Output the font definitions for all fonts that were used@>; + dvi_out(post_post);dvi_four(last_bop);dvi_out(id_byte);@/ + k=4+((dvi_buf_size-dvi_ptr)%4); /*the number of 223's*/ + while (k > 0) + {@+dvi_out(223);decr(k); + } + @<Empty the last bytes out of |dvi_buf|@>; + print_nl("Output written on ");slow_print(output_file_name); +@.Output written on x@> + print(" (");print_int(total_pages);print(" page"); + if (total_pages!=1) print_char('s'); + print(", ");print_int(dvi_offset+dvi_ptr);print(" bytes)."); + b_close(&dvi_file); + } + +@ @<Output the font definitions...@>= +while (font_ptr > font_base) + {@+if (font_used[font_ptr]) dvi_font_def(font_ptr); + decr(font_ptr); + } + +@* Packaging. +We're essentially done with the parts of \TeX\ that are concerned with +the input (|get_next|) and the output (|ship_out|). So it's time to +get heavily into the remaining part, which does the real work of typesetting. + +After lists are constructed, \TeX\ wraps them up and puts them into boxes. +Two major subroutines are given the responsibility for this task: |hpack| +applies to horizontal lists (hlists) and |vpack| applies to vertical lists +(vlists). The main duty of |hpack| and |vpack| is to compute the dimensions +of the resulting boxes, and to adjust the glue if one of those dimensions +is pre-specified. The computed sizes normally enclose all of the material +inside the new box; but some items may stick out if negative glue is used, +if the box is overfull, or if a \.{\\vbox} includes other boxes that have +been shifted left. + +The subroutine call |hpack(p, w, m)| returns a pointer to an |hlist_node| +for a box containing the hlist that starts at |p|. Parameter |w| specifies +a width; and parameter |m| is either `|exactly|' or `|additional|'. Thus, +|hpack(p, w, exactly)| produces a box whose width is exactly |w|, while +|hpack(p, w, additional)| yields a box whose width is the natural width plus +|w|. It is convenient to define a macro called `|natural|' to cover the +most common case, so that we can say |hpack(p, natural)| to get a box that +has the natural width of list |p|. + +Similarly, |vpack(p, w, m)| returns a pointer to a |vlist_node| for a +box containing the vlist that starts at |p|. In this case |w| represents +a height instead of a width; the parameter |m| is interpreted as in |hpack|. + +@d exactly 0 /*a box dimension is pre-specified*/ +@d additional 1 /*a box dimension is increased from the natural one*/ +@d natural 0, additional /*shorthand for parameters to |hpack| and |vpack|*/ + +@ The parameters to |hpack| and |vpack| correspond to \TeX's primitives +like `\.{\\hbox} \.{to} \.{300pt}', `\.{\\hbox} \.{spread} \.{10pt}'; note +that `\.{\\hbox}' with no dimension following it is equivalent to +`\.{\\hbox} \.{spread} \.{0pt}'. The |scan_spec| subroutine scans such +constructions in the user's input, including the mandatory left brace that +follows them, and it puts the specification onto |save_stack| so that the +desired box can later be obtained by executing the following code: +$$\vbox{\halign{#\hfil\cr +|save_ptr=save_ptr-2;|\cr +|hpack(p, saved(1), saved(0)).|\cr}}$$ +Special care is necessary to ensure that the special |save_stack| codes +are placed just below the new group code, because scanning can change +|save_stack| when \.{\\csname} appears. + +@p static void scan_spec(group_code @!c, bool @!three_codes) + /*scans a box specification and left brace*/ +{@+ +int @!s; /*temporarily saved value*/ +int @!spec_code; +if (three_codes) s=saved(0); +if (scan_keyword("to")) spec_code=exactly; +@.to@> +else if (scan_keyword("spread")) spec_code=additional; +@.spread@> +else{@+spec_code=additional;cur_val=0; + goto found; + } +scan_normal_dimen; +found: if (three_codes) + {@+saved(0)=s;incr(save_ptr); + } +saved(0)=spec_code;saved(1)=cur_val;save_ptr=save_ptr+2; +new_save_level(c);scan_left_brace(); +} + +@ To figure out the glue setting, |hpack| and |vpack| determine how much +stretchability and shrinkability are present, considering all four orders +of infinity. The highest order of infinity that has a nonzero coefficient +is then used as if no other orders were present. + +For example, suppose that the given list contains six glue nodes with +the respective stretchabilities 3pt, 8fill, 5fil, 6pt, $-3$fil, $-8$fill. +Then the total is essentially 2fil; and if a total additional space of 6pt +is to be achieved by stretching, the actual amounts of stretch will be +0pt, 0pt, 15pt, 0pt, $-9$pt, and 0pt, since only `fil' glue will be +considered. (The `fill' glue is therefore not really stretching infinitely +with respect to `fil'; nobody would actually want that to happen.) + +The arrays |total_stretch| and |total_shrink| are used to determine how much +glue of each kind is present. A global variable |last_badness| is used +to implement \.{\\badness}. + +@<Glob...@>= +static scaled @!total_stretch0[filll-normal+1], + *const @!total_stretch = @!total_stretch0-normal, + @!total_shrink0[filll-normal+1], *const @!total_shrink = @!total_shrink0-normal; + /*glue found by |hpack| or |vpack|*/ +static int @!last_badness; /*badness of the most recently packaged box*/ + +@ If the global variable |adjust_tail| is non-null, the |hpack| routine +also removes all occurrences of |ins_node|, |mark_node|, and |adjust_node| +items and appends the resulting material onto the list that ends at +location |adjust_tail|. + +@<Glob...@>= +static pointer @!adjust_tail; /*tail of adjustment list*/ + +@ @<Set init...@>=adjust_tail=null;last_badness=0; + +@ Here now is |hpack|, which contains few if any surprises. + +@p static pointer hpack(pointer @!p, scaled @!w, small_number @!m) +{@+ +pointer r; /*the box node that will be returned*/ +pointer @!q; /*trails behind |p|*/ +scaled @!h, @!d, @!x; /*height, depth, and natural width*/ +scaled @!s; /*shift amount*/ +pointer @!g; /*points to a glue specification*/ +glue_ord @!o; /*order of infinity*/ +internal_font_number @!f; /*the font in a |char_node|*/ +four_quarters @!i; /*font information about a |char_node|*/ +eight_bits @!hd; /*height and depth indices for a character*/ +last_badness=0;r=get_node(box_node_size);type(r)=hlist_node; +subtype(r)=min_quarterword;shift_amount(r)=0; +q=r+list_offset;link(q)=p;@/ +h=0;@<Clear dimensions to zero@>; +while (p!=null) @<Examine node |p| in the hlist, taking account of its effect +on the dimensions of the new box, or moving it to the adjustment list; then +advance |p| to the next node@>; +if (adjust_tail!=null) link(adjust_tail)=null; +height(r)=h;depth(r)=d;@/ +@<Determine the value of |width(r)| and the appropriate glue setting; then +|return| or |goto common_ending|@>; +common_ending: @<Finish issuing a diagnostic message for an overfull or underfull +hbox@>; +end: return r; +} + +@ @<Clear dimensions to zero@>= +d=0;x=0; +total_stretch[normal]=0;total_shrink[normal]=0; +total_stretch[fil]=0;total_shrink[fil]=0; +total_stretch[fill]=0;total_shrink[fill]=0; +total_stretch[filll]=0;total_shrink[filll]=0 + +@ @<Examine node |p| in the hlist, taking account of its effect...@>= +@^inner loop@> +{@+reswitch: while (is_char_node(p)) + @<Incorporate character dimensions into the dimensions of the hbox that +will contain~it, then move to the next node@>; +if (p!=null) + {@+switch (type(p)) { + case hlist_node: case vlist_node: case rule_node: + case unset_node: + @<Incorporate box dimensions into the dimensions of the hbox that will +contain~it@>@;@+break; + case ins_node: case mark_node: case adjust_node: if (adjust_tail!=null) + @<Transfer node |p| to the adjustment list@>@;@+break; + case whatsit_node: @<Incorporate a whatsit node into an hbox@>;@+break; + case glue_node: @<Incorporate glue into the horizontal totals@>@;@+break; + case kern_node: case math_node: x=x+width(p);@+break; + case ligature_node: @<Make node |p| look like a |char_node| and |goto reswitch|@>@; + default:do_nothing; + } @/ + p=link(p); + } +} + + +@ @<Make node |p| look like a |char_node| and |goto reswitch|@>= +{@+mem[lig_trick]=mem[lig_char(p)];link(lig_trick)=link(p); +p=lig_trick;goto reswitch; +} + +@ The code here implicitly uses the fact that running dimensions are +indicated by |null_flag|, which will be ignored in the calculations +because it is a highly negative number. + +@<Incorporate box dimensions into the dimensions of the hbox...@>= +{@+x=x+width(p); +if (type(p) >= rule_node) s=0;@+else s=shift_amount(p); +if (height(p)-s > h) h=height(p)-s; +if (depth(p)+s > d) d=depth(p)+s; +} + +@ The following code is part of \TeX's inner loop; i.e., adding another +character of text to the user's input will cause each of these instructions +to be exercised one more time. +@^inner loop@> + +@<Incorporate character dimensions into the dimensions of the hbox...@>= +{@+f=font(p);i=char_info(f, character(p));hd=height_depth(i); +x=x+char_width(f, i);@/ +s=char_height(f, hd);@+if (s > h) h=s; +s=char_depth(f, hd);@+if (s > d) d=s; +p=link(p); +} + +@ Although node |q| is not necessarily the immediate predecessor of node |p|, +it always points to some node in the list preceding |p|. Thus, we can delete +nodes by moving |q| when necessary. The algorithm takes linear time, and the +extra computation does not intrude on the inner loop unless it is necessary +to make a deletion. +@^inner loop@> + +@<Transfer node |p| to the adjustment list@>= +{@+while (link(q)!=p) q=link(q); +if (type(p)==adjust_node) + {@+link(adjust_tail)=adjust_ptr(p); + while (link(adjust_tail)!=null) adjust_tail=link(adjust_tail); + p=link(p);free_node(link(q), small_node_size); + } +else{@+link(adjust_tail)=p;adjust_tail=p;p=link(p); + } +link(q)=p;p=q; +} + +@ @<Incorporate glue into the horizontal totals@>= +{@+g=glue_ptr(p);x=x+width(g);@/ +o=stretch_order(g);total_stretch[o]=total_stretch[o]+stretch(g); +o=shrink_order(g);total_shrink[o]=total_shrink[o]+shrink(g); +if (subtype(p) >= a_leaders) + {@+g=leader_ptr(p); + if (height(g) > h) h=height(g); + if (depth(g) > d) d=depth(g); + } +} + +@ When we get to the present part of the program, |x| is the natural width +of the box being packaged. + +@<Determine the value of |width(r)| and the appropriate glue setting...@>= +if (m==additional) w=x+w; +width(r)=w;x=w-x; /*now |x| is the excess to be made up*/ +if (x==0) + {@+glue_sign(r)=normal;glue_order(r)=normal; + set_glue_ratio_zero(glue_set(r)); + goto end; + } +else if (x > 0) @<Determine horizontal glue stretch setting, then |return| +or \hbox{|goto common_ending|}@>@; +else@<Determine horizontal glue shrink setting, then |return| or \hbox{|goto +common_ending|}@>@; + +@ @<Determine horizontal glue stretch setting...@>= +{@+@<Determine the stretch order@>; +glue_order(r)=o;glue_sign(r)=stretching; +if (total_stretch[o]!=0) glue_set(r)=fix(x/(double)total_stretch[o]); +@^real division@> +else{@+glue_sign(r)=normal; + set_glue_ratio_zero(glue_set(r)); /*there's nothing to stretch*/ + } +if (o==normal) if (list_ptr(r)!=null) + @<Report an underfull hbox and |goto common_ending|, if this box is sufficiently +bad@>; +goto end; +} + +@ @<Determine the stretch order@>= +if (total_stretch[filll]!=0) o=filll; +else if (total_stretch[fill]!=0) o=fill; +else if (total_stretch[fil]!=0) o=fil; +else o=normal + +@ @<Report an underfull hbox and |goto common_ending|, if...@>= +{@+last_badness=badness(x, total_stretch[normal]); +if (last_badness > hbadness) + {@+print_ln(); + if (last_badness > 100) print_nl("Underfull");@+else print_nl("Loose"); + print(" \\hbox (badness ");print_int(last_badness); +@.Underfull \\hbox...@> +@.Loose \\hbox...@> + goto common_ending; + } +} + +@ In order to provide a decent indication of where an overfull or underfull +box originated, we use a global variable |pack_begin_line| that is +set nonzero only when |hpack| is being called by the paragraph builder +or the alignment finishing routine. + +@<Glob...@>= +static int @!pack_begin_line; /*source file line where the current paragraph + or alignment began; a negative value denotes alignment*/ + +@ @<Set init...@>= +pack_begin_line=0; + +@ @<Finish issuing a diagnostic message for an overfull or underfull hbox@>= +if (output_active) print(") has occurred while \\output is active"); +else{@+if (pack_begin_line!=0) + {@+if (pack_begin_line > 0) print(") in paragraph at lines "); + else print(") in alignment at lines "); + print_int(abs(pack_begin_line)); + print("--"); + } + else print(") detected at line "); + print_int(line); + } +print_ln();@/ +font_in_short_display=null_font;short_display(list_ptr(r));print_ln();@/ +begin_diagnostic();show_box(r);end_diagnostic(true) + +@ @<Determine horizontal glue shrink setting...@>= +{@+@<Determine the shrink order@>; +glue_order(r)=o;glue_sign(r)=shrinking; +if (total_shrink[o]!=0) glue_set(r)=fix((-x)/(double)total_shrink[o]); +@^real division@> +else{@+glue_sign(r)=normal; + set_glue_ratio_zero(glue_set(r)); /*there's nothing to shrink*/ + } +if ((total_shrink[o] < -x)&&(o==normal)&&(list_ptr(r)!=null)) + {@+last_badness=1000000; + set_glue_ratio_one(glue_set(r)); /*use the maximum shrinkage*/ + @<Report an overfull hbox and |goto common_ending|, if this box is sufficiently +bad@>; + } +else if (o==normal) if (list_ptr(r)!=null) + @<Report a tight hbox and |goto common_ending|, if this box is sufficiently +bad@>; +goto end; +} + +@ @<Determine the shrink order@>= +if (total_shrink[filll]!=0) o=filll; +else if (total_shrink[fill]!=0) o=fill; +else if (total_shrink[fil]!=0) o=fil; +else o=normal + +@ @<Report an overfull hbox and |goto common_ending|, if...@>= +if ((-x-total_shrink[normal] > hfuzz)||(hbadness < 100)) + {@+if ((overfull_rule > 0)&&(-x-total_shrink[normal] > hfuzz)) + {@+while (link(q)!=null) q=link(q); + link(q)=new_rule(); + width(link(q))=overfull_rule; + } + print_ln();print_nl("Overfull \\hbox ("); +@.Overfull \\hbox...@> + print_scaled(-x-total_shrink[normal]);print("pt too wide"); + goto common_ending; + } + +@ @<Report a tight hbox and |goto common_ending|, if...@>= +{@+last_badness=badness(-x, total_shrink[normal]); +if (last_badness > hbadness) + {@+print_ln();print_nl("Tight \\hbox (badness ");print_int(last_badness); +@.Tight \\hbox...@> + goto common_ending; + } +} + +@ The |vpack| subroutine is actually a special case of a slightly more +general routine called |vpackage|, which has four parameters. The fourth +parameter, which is |max_dimen| in the case of |vpack|, specifies the +maximum depth of the page box that is constructed. The depth is first +computed by the normal rules; if it exceeds this limit, the reference +point is simply moved down until the limiting depth is attained. + + +@p +#define vpack(...) @[vpackage(__VA_ARGS__, max_dimen)@] /*special case of unconstrained depth*/ +static pointer vpackage(pointer @!p, scaled @!h, small_number @!m, scaled @!l) +{@+ +pointer r; /*the box node that will be returned*/ +scaled @!w, @!d, @!x; /*width, depth, and natural height*/ +scaled @!s; /*shift amount*/ +pointer @!g; /*points to a glue specification*/ +glue_ord @!o; /*order of infinity*/ +last_badness=0;r=get_node(box_node_size);type(r)=vlist_node; +subtype(r)=min_quarterword;shift_amount(r)=0; +list_ptr(r)=p;@/ +w=0;@<Clear dimensions to zero@>; +while (p!=null) @<Examine node |p| in the vlist, taking account of its effect +on the dimensions of the new box; then advance |p| to the next node@>; +width(r)=w; +if (d > l) + {@+x=x+d-l;depth(r)=l; + } +else depth(r)=d; +@<Determine the value of |height(r)| and the appropriate glue setting; then +|return| or |goto common_ending|@>; +common_ending: @<Finish issuing a diagnostic message for an overfull or underfull +vbox@>; +end: return r; +} + +@ @<Examine node |p| in the vlist, taking account of its effect...@>= +{@+if (is_char_node(p)) confusion("vpack"); +@:this can't happen vpack}{\quad vpack@> +else switch (type(p)) { + case hlist_node: case vlist_node: case rule_node: + case unset_node: + @<Incorporate box dimensions into the dimensions of the vbox that will +contain~it@>@;@+break; + case whatsit_node: @<Incorporate a whatsit node into a vbox@>;@+break; + case glue_node: @<Incorporate glue into the vertical totals@>@;@+break; + case kern_node: {@+x=x+d+width(p);d=0; + } @+break; + default:do_nothing; + } +p=link(p); +} + +@ @<Incorporate box dimensions into the dimensions of the vbox...@>= +{@+x=x+d+height(p);d=depth(p); +if (type(p) >= rule_node) s=0;@+else s=shift_amount(p); +if (width(p)+s > w) w=width(p)+s; +} + +@ @<Incorporate glue into the vertical totals@>= +{@+x=x+d;d=0;@/ +g=glue_ptr(p);x=x+width(g);@/ +o=stretch_order(g);total_stretch[o]=total_stretch[o]+stretch(g); +o=shrink_order(g);total_shrink[o]=total_shrink[o]+shrink(g); +if (subtype(p) >= a_leaders) + {@+g=leader_ptr(p); + if (width(g) > w) w=width(g); + } +} + +@ When we get to the present part of the program, |x| is the natural height +of the box being packaged. + +@<Determine the value of |height(r)| and the appropriate glue setting...@>= +if (m==additional) h=x+h; +height(r)=h;x=h-x; /*now |x| is the excess to be made up*/ +if (x==0) + {@+glue_sign(r)=normal;glue_order(r)=normal; + set_glue_ratio_zero(glue_set(r)); + goto end; + } +else if (x > 0) @<Determine vertical glue stretch setting, then |return| or +\hbox{|goto common_ending|}@>@; +else@<Determine vertical glue shrink setting, then |return| or \hbox{|goto +common_ending|}@>@; + +@ @<Determine vertical glue stretch setting...@>= +{@+@<Determine the stretch order@>; +glue_order(r)=o;glue_sign(r)=stretching; +if (total_stretch[o]!=0) glue_set(r)=fix(x/(double)total_stretch[o]); +@^real division@> +else{@+glue_sign(r)=normal; + set_glue_ratio_zero(glue_set(r)); /*there's nothing to stretch*/ + } +if (o==normal) if (list_ptr(r)!=null) + @<Report an underfull vbox and |goto common_ending|, if this box is sufficiently +bad@>; +goto end; +} + +@ @<Report an underfull vbox and |goto common_ending|, if...@>= +{@+last_badness=badness(x, total_stretch[normal]); +if (last_badness > vbadness) + {@+print_ln(); + if (last_badness > 100) print_nl("Underfull");@+else print_nl("Loose"); + print(" \\vbox (badness ");print_int(last_badness); +@.Underfull \\vbox...@> +@.Loose \\vbox...@> + goto common_ending; + } +} + +@ @<Finish issuing a diagnostic message for an overfull or underfull vbox@>= +if (output_active) print(") has occurred while \\output is active"); +else{@+if (pack_begin_line!=0) /*it's actually negative*/ + {@+print(") in alignment at lines "); + print_int(abs(pack_begin_line)); + print("--"); + } + else print(") detected at line "); + print_int(line); + print_ln();@/ + } +begin_diagnostic();show_box(r);end_diagnostic(true) + +@ @<Determine vertical glue shrink setting...@>= +{@+@<Determine the shrink order@>; +glue_order(r)=o;glue_sign(r)=shrinking; +if (total_shrink[o]!=0) glue_set(r)=fix((-x)/(double)total_shrink[o]); +@^real division@> +else{@+glue_sign(r)=normal; + set_glue_ratio_zero(glue_set(r)); /*there's nothing to shrink*/ + } +if ((total_shrink[o] < -x)&&(o==normal)&&(list_ptr(r)!=null)) + {@+last_badness=1000000; + set_glue_ratio_one(glue_set(r)); /*use the maximum shrinkage*/ + @<Report an overfull vbox and |goto common_ending|, if this box is sufficiently +bad@>; + } +else if (o==normal) if (list_ptr(r)!=null) + @<Report a tight vbox and |goto common_ending|, if this box is sufficiently +bad@>; +goto end; +} + +@ @<Report an overfull vbox and |goto common_ending|, if...@>= +if ((-x-total_shrink[normal] > vfuzz)||(vbadness < 100)) + {@+print_ln();print_nl("Overfull \\vbox ("); +@.Overfull \\vbox...@> + print_scaled(-x-total_shrink[normal]);print("pt too high"); + goto common_ending; + } + +@ @<Report a tight vbox and |goto common_ending|, if...@>= +{@+last_badness=badness(-x, total_shrink[normal]); +if (last_badness > vbadness) + {@+print_ln();print_nl("Tight \\vbox (badness ");print_int(last_badness); +@.Tight \\vbox...@> + goto common_ending; + } +} + +@ When a box is being appended to the current vertical list, the +baselineskip calculation is handled by the |append_to_vlist| routine. + +@p static void append_to_vlist(pointer @!b) +{@+scaled d; /*deficiency of space between baselines*/ +pointer @!p; /*a new glue node*/ +if (prev_depth > ignore_depth) + {@+d=width(baseline_skip)-prev_depth-height(b); + if (d < line_skip_limit) p=new_param_glue(line_skip_code); + else{@+p=new_skip_param(baseline_skip_code); + width(temp_ptr)=d; /*|temp_ptr==glue_ptr(p)|*/ + } + link(tail)=p;tail=p; + } +link(tail)=b;tail=b;prev_depth=depth(b); +} + +@* Data structures for math mode. +When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an +{\sl mlist}, which is essentially a tree structure representing that +formula. An mlist is a linear sequence of items, but we can regard it as +a tree structure because mlists can appear within mlists. For example, many +of the entries can be subscripted or superscripted, and such ``scripts'' +are mlists in their own right. + +An entire formula is parsed into such a tree before any of the actual +typesetting is done, because the current style of type is usually not +known until the formula has been fully scanned. For example, when the +formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell +that `\.{a+b}' will be in script size until `\.{\\over}' has appeared. + +During the scanning process, each element of the mlist being built is +classified as a relation, a binary operator, an open parenthesis, etc., +or as a construct like `\.{\\sqrt}' that must be built up. This classification +appears in the mlist data structure. + +After a formula has been fully scanned, the mlist is converted to an hlist +so that it can be incorporated into the surrounding text. This conversion is +controlled by a recursive procedure that decides all of the appropriate +styles by a ``top-down'' process starting at the outermost level and working +in towards the subformulas. The formula is ultimately pasted together using +combinations of horizontal and vertical boxes, with glue and penalty nodes +inserted as necessary. + +An mlist is represented internally as a linked list consisting chiefly +of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat +similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are +allowed to appear in mlists together with the noads; \TeX\ tells the difference +by means of the |type| field, since a noad's |type| is always greater than +that of a node. An mlist does not contain character nodes, hlist nodes, vlist +nodes, math nodes, ligature nodes, +or unset nodes; in particular, each mlist item appears in the +variable-size part of |mem|, so the |type| field is always present. + +@ Each noad is four or more words long. The first word contains the |type| +and |subtype| and |link| fields that are already so familiar to us; the +second, third, and fourth words are called the noad's |nucleus|, |subscr|, +and |supscr| fields. + +Consider, for example, the simple formula `\.{\$x\^2\$}', which would be +parsed into an mlist containing a single element called an |ord_noad|. +The |nucleus| of this noad is a representation of `\.x', the |subscr| is +empty, and the |supscr| is a representation of `\.2'. + +The |nucleus|, |subscr|, and |supscr| fields are further broken into +subfields. If |p| points to a noad, and if |q| is one of its principal +fields (e.g., |q==subscr(p)|), there are several possibilities for the +subfields, depending on the |math_type| of |q|. + +\yskip\hang|math_type(q)==math_char| means that |fam(q)| refers to one of +the sixteen font families, and |character(q)| is the number of a character +within a font of that family, as in a character node. + +\yskip\hang|math_type(q)==math_text_char| is similar, but the character is +unsubscripted and unsuperscripted and it is followed immediately by another +character from the same font. (This |math_type| setting appears only +briefly during the processing; it is used to suppress unwanted italic +corrections.) + +\yskip\hang|math_type(q)==empty| indicates a field with no value (the +corresponding attribute of noad |p| is not present). + +\yskip\hang|math_type(q)==sub_box| means that |info(q)| points to a box +node (either an |hlist_node| or a |vlist_node|) that should be used as the +value of the field. The |shift_amount| in the subsidiary box node is the +amount by which that box will be shifted downward. + +\yskip\hang|math_type(q)==sub_mlist| means that |info(q)| points to +an mlist; the mlist must be converted to an hlist in order to obtain +the value of this field. + +\yskip\noindent In the latter case, we might have |info(q)==null|. This +is not the same as |math_type(q)==empty|; for example, `\.{\$P\_\{\}\$}' +and `\.{\$P\$}' produce different results (the former will not have the +``italic correction'' added to the width of |P|, but the ``script skip'' +will be added). + +The definitions of subfields given here are evidently wasteful of space, +since a halfword is being used for the |math_type| although only three +bits would be needed. However, there are hardly ever many noads present at +once, since they are soon converted to nodes that take up even more space, +so we can afford to represent them in whatever way simplifies the +programming. + +@d noad_size 4 /*number of words in a normal noad*/ +@d nucleus(A) A+1 /*the |nucleus| field of a noad*/ +@d supscr(A) A+2 /*the |supscr| field of a noad*/ +@d subscr(A) A+3 /*the |subscr| field of a noad*/ +@d math_type(A) link(A) /*a |halfword| in |mem|*/ +@d fam font /*a |quarterword| in |mem|*/ +@d math_char 1 /*|math_type| when the attribute is simple*/ +@d sub_box 2 /*|math_type| when the attribute is a box*/ +@d sub_mlist 3 /*|math_type| when the attribute is a formula*/ +@d math_text_char 4 /*|math_type| when italic correction is dubious*/ + +@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Open, +Close, Punct, or Inner, for purposes of spacing and line breaking. An +|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, +|punct_noad|, or |inner_noad| is used to represent portions of the various +types. For example, an `\.=' sign in a formula leads to the creation of a +|rel_noad| whose |nucleus| field is a representation of an equals sign +(usually |fam==0|, |character==075|). A formula preceded by \.{\\mathrel} +also results in a |rel_noad|. When a |rel_noad| is followed by an +|op_noad|, say, and possibly separated by one or more ordinary nodes (not +noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) +just after the formula that corresponds to the |rel_noad|, unless there +already was a penalty immediately following; and a ``thick space'' will be +inserted just before the formula that corresponds to the |op_noad|. + +A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually +has a |subtype==normal|. The only exception is that an |op_noad| might +have |subtype==limits| or |no_limits|, if the normal positioning of +limits has been overridden for this operator. + +@d ord_noad (unset_node+3) /*|type| of a noad classified Ord*/ +@d op_noad (ord_noad+1) /*|type| of a noad classified Op*/ +@d bin_noad (ord_noad+2) /*|type| of a noad classified Bin*/ +@d rel_noad (ord_noad+3) /*|type| of a noad classified Rel*/ +@d open_noad (ord_noad+4) /*|type| of a noad classified Open*/ +@d close_noad (ord_noad+5) /*|type| of a noad classified Close*/ +@d punct_noad (ord_noad+6) /*|type| of a noad classified Punct*/ +@d inner_noad (ord_noad+7) /*|type| of a noad classified Inner*/ +@d limits 1 /*|subtype| of |op_noad| whose scripts are to be above, below*/ +@d no_limits 2 /*|subtype| of |op_noad| whose scripts are to be normal*/ + +@ A |radical_noad| is five words long; the fifth word is the |left_delimiter| +field, which usually represents a square root sign. + +A |fraction_noad| is six words long; it has a |right_delimiter| field +as well as a |left_delimiter|. + +Delimiter fields are of type |four_quarters|, and they have four subfields +called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields +represent variable-size delimiters by giving the ``small'' and ``large'' +starting characters, as explained in Chapter~17 of {\sl The \TeX book}. +@:TeXbook}{\sl The \TeX book@> + +A |fraction_noad| is actually quite different from all other noads. Not +only does it have six words, it has |thickness|, |denominator|, and +|numerator| fields instead of |nucleus|, |subscr|, and |supscr|. The +|thickness| is a scaled value that tells how thick to make a fraction +rule; however, the special value |default_code| is used to stand for the +|default_rule_thickness| of the current size. The |numerator| and +|denominator| point to mlists that define a fraction; we always have +$$\hbox{|math_type(numerator)==math_type(denominator)==sub_mlist|}.$$ The +|left_delimiter| and |right_delimiter| fields specify delimiters that will +be placed at the left and right of the fraction. In this way, a +|fraction_noad| is able to represent all of \TeX's operators \.{\\over}, +\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and + \.{\\abovewithdelims}. + +@d left_delimiter(A) A+4 /*first delimiter field of a noad*/ +@d right_delimiter(A) A+5 /*second delimiter field of a fraction noad*/ +@d radical_noad (inner_noad+1) /*|type| of a noad for square roots*/ +@d radical_noad_size 5 /*number of |mem| words in a radical noad*/ +@d fraction_noad (radical_noad+1) /*|type| of a noad for generalized fractions*/ +@d fraction_noad_size 6 /*number of |mem| words in a fraction noad*/ +@d small_fam(A) mem[A].qqqq.b0 /*|fam| for ``small'' delimiter*/ +@d small_char(A) mem[A].qqqq.b1 /*|character| for ``small'' delimiter*/ +@d large_fam(A) mem[A].qqqq.b2 /*|fam| for ``large'' delimiter*/ +@d large_char(A) mem[A].qqqq.b3 /*|character| for ``large'' delimiter*/ +@d thickness(A) width(A) /*|thickness| field in a fraction noad*/ +@d default_code 010000000000 /*denotes |default_rule_thickness|*/ +@d numerator(A) supscr(A) /*|numerator| field in a fraction noad*/ +@d denominator(A) subscr(A) /*|denominator| field in a fraction noad*/ + +@ The global variable |empty_field| is set up for initialization of empty +fields in new noads. Similarly, |null_delimiter| is for the initialization +of delimiter fields. + +@<Glob...@>= +static two_halves @!empty_field; +static four_quarters @!null_delimiter; + +@ @<Set init...@>= +empty_field.rh=empty;empty_field.lh=null;@/ +null_delimiter.b0=0;null_delimiter.b1=min_quarterword;@/ +null_delimiter.b2=0;null_delimiter.b3=min_quarterword; + +@ The |new_noad| function creates an |ord_noad| that is completely null. + +@p static pointer new_noad(void) +{@+pointer p; +p=get_node(noad_size); +type(p)=ord_noad;subtype(p)=normal; +mem[nucleus(p)].hh=empty_field; +mem[subscr(p)].hh=empty_field; +mem[supscr(p)].hh=empty_field; +return p; +} + +@ A few more kinds of noads will complete the set: An |under_noad| has its +nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places +an accent over its nucleus; the accent character appears as +|fam(accent_chr(p))| and |character(accent_chr(p))|. A |vcenter_noad| +centers its nucleus vertically with respect to the axis of the formula; +in such noads we always have |math_type(nucleus(p))==sub_box|. + +And finally, we have |left_noad| and |right_noad| types, to implement +\TeX's \.{\\left} and \.{\\right} as well as \eTeX's \.{\\middle}. +The |nucleus| of such noads is +replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces +a |left_noad| such that |delimiter(p)| holds the family and character +codes for all left parentheses. A |left_noad| never appears in an mlist +except as the first element, and a |right_noad| never appears in an mlist +except as the last element; furthermore, we either have both a |left_noad| +and a |right_noad|, or neither one is present. The |subscr| and |supscr| +fields are always |empty| in a |left_noad| and a |right_noad|. + +@d under_noad (fraction_noad+1) /*|type| of a noad for underlining*/ +@d over_noad (under_noad+1) /*|type| of a noad for overlining*/ +@d accent_noad (over_noad+1) /*|type| of a noad for accented subformulas*/ +@d accent_noad_size 5 /*number of |mem| words in an accent noad*/ +@d accent_chr(A) A+4 /*the |accent_chr| field of an accent noad*/ +@d vcenter_noad (accent_noad+1) /*|type| of a noad for \.{\\vcenter}*/ +@d left_noad (vcenter_noad+1) /*|type| of a noad for \.{\\left}*/ +@d right_noad (left_noad+1) /*|type| of a noad for \.{\\right}*/ +@d delimiter(A) nucleus(A) /*|delimiter| field in left and right noads*/ +@d middle_noad 1 /*|subtype| of right noad representing \.{\\middle}*/ +@d scripts_allowed(A) (type(A) >= ord_noad)&&(type(A) < left_noad) + +@ Math formulas can also contain instructions like \.{\\textstyle} that +override \TeX's normal style rules. A |style_node| is inserted into the +data structure to record such instructions; it is three words long, so it +is considered a node instead of a noad. The |subtype| is either |display_style| +or |text_style| or |script_style| or |script_script_style|. The +second and third words of a |style_node| are not used, but they are +present because a |choice_node| is converted to a |style_node|. + +\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles +|display_style|, \dots, |script_script_style|, and adds~1 to get the +``cramped'' versions of these styles. This gives a numerical order that +is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; +i.e., a smaller style has a larger numerical value. +@:TeXbook}{\sl The \TeX book@> + +@d style_node (unset_node+1) /*|type| of a style node*/ +@d style_node_size 3 /*number of words in a style node*/ +@d display_style 0 /*|subtype| for \.{\\displaystyle}*/ +@d text_style 2 /*|subtype| for \.{\\textstyle}*/ +@d script_style 4 /*|subtype| for \.{\\scriptstyle}*/ +@d script_script_style 6 /*|subtype| for \.{\\scriptscriptstyle}*/ +@d cramped 1 /*add this to an uncramped style if you want to cramp it*/ + +@p static pointer new_style(small_number @!s) /*create a style node*/ +{@+pointer p; /*the new node*/ +p=get_node(style_node_size);type(p)=style_node; +subtype(p)=s;width(p)=0;depth(p)=0; /*the |width| and |depth| are not used*/ +return p; +} + +@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which +has special subfields |display_mlist|, |text_mlist|, |script_mlist|, +and |script_script_mlist| pointing to the mlists for each style. + +@d choice_node (unset_node+2) /*|type| of a choice node*/ +@d display_mlist(A) info(A+1) /*mlist to be used in display style*/ +@d text_mlist(A) link(A+1) /*mlist to be used in text style*/ +@d script_mlist(A) info(A+2) /*mlist to be used in script style*/ +@d script_script_mlist(A) link(A+2) /*mlist to be used in scriptscript style*/ + +@p static pointer new_choice(void) /*create a choice node*/ +{@+pointer p; /*the new node*/ +p=get_node(style_node_size);type(p)=choice_node; +subtype(p)=0; /*the |subtype| is not used*/ +display_mlist(p)=null;text_mlist(p)=null;script_mlist(p)=null; +script_script_mlist(p)=null; +return p; +} + +@ Let's consider now the previously unwritten part of |show_node_list| +that displays the things that can only be present in mlists; this +program illustrates how to access the data structures just defined. + +In the context of the following program, |p| points to a node or noad that +should be displayed, and the current string contains the ``recursion history'' +that leads to this point. The recursion history consists of a dot for each +outer level in which |p| is subsidiary to some node, or in which |p| is +subsidiary to the |nucleus| field of some noad; the dot is replaced by +`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| +or |supscr| or |denominator| or |numerator| fields of noads. For example, +the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for +|x| in the (ridiculous) formula +`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. + +@<Cases of |show_node_list| that arise...@>= +case style_node: print_style(subtype(p));@+break; +case choice_node: @<Display choice node |p|@>@;@+break; +case ord_noad: case op_noad: case bin_noad: + case rel_noad: case open_noad: case close_noad: + case punct_noad: case inner_noad: + case radical_noad: case over_noad: case under_noad: + case vcenter_noad: case accent_noad: + case left_noad: case right_noad: @<Display normal noad |p|@>@;@+break; +case fraction_noad: @<Display fraction noad |p|@>@;@+break; + +@ Here are some simple routines used in the display of noads. + +@<Declare procedures needed for displaying the elements of mlists@>= +static void print_fam_and_char(pointer @!p) /*prints family and character*/ +{@+print_esc("fam");print_int(fam(p));print_char(' '); +print_ASCII(qo(character(p))); +} +@# +static void print_delimiter(pointer @!p) /*prints a delimiter as 24-bit hex value*/ +{@+int a; /*accumulator*/ +a=small_fam(p)*256+qo(small_char(p)); +a=a*0x1000+large_fam(p)*256+qo(large_char(p)); +if (a < 0) print_int(a); /*this should never happen*/ +else print_hex(a); +} + +@ The next subroutine will descend to another level of recursion when a +subsidiary mlist needs to be displayed. The parameter |c| indicates what +character is to become part of the recursion history. An empty mlist is +distinguished from a field with |math_type(p)==empty|, because these are +not equivalent (as explained above). +@^recursion@> + +@<Declare procedures needed for displaying...@>= +static void show_info(void); /*|show_node_list(info(temp_ptr))|*/ +static void print_subsidiary_data(pointer @!p, ASCII_code @!c) + /*display a noad field*/ +{@+if (cur_length >= depth_threshold) + {@+if (math_type(p)!=empty) print(" []"); + } +else{@+append_char(c); /*include |c| in the recursion history*/ + temp_ptr=p; /*prepare for |show_info| if recursion is needed*/ + switch (math_type(p)) { + case math_char: {@+print_ln();print_current_string();print_fam_and_char(p); + } @+break; + case sub_box: show_info();@+break; /*recursive call*/ + case sub_mlist: if (info(p)==null) + {@+print_ln();print_current_string();print("{}"); + } + else show_info();@+break; /*recursive call*/ + default:do_nothing; /*|empty|*/ + } @/ + flush_char; /*remove |c| from the recursion history*/ + } +} + +@ The inelegant introduction of |show_info| in the code above seems better +than the alternative of using \PASCAL's strange |forward| declaration for a +procedure with parameters. The \PASCAL\ convention about dropping parameters +from a post-|forward| procedure is, frankly, so intolerable to the author +of \TeX\ that he would rather stoop to communication via a global temporary +variable. (A similar stoopidity occurred with respect to |hlist_out| and +|vlist_out| above, and it will occur with respect to |mlist_to_hlist| below.) +@^Knuth, Donald Ervin@> +@:PASCAL}{\PASCAL@> + +@p static void show_info(void) /*the reader will kindly forgive this*/ +{@+show_node_list(info(temp_ptr)); +} + +@ @<Declare procedures needed for displaying...@>= +static void print_style(int @!c) +{@+switch (c/2) { +case 0: print_esc("displaystyle");@+break; /*|display_style==0|*/ +case 1: print_esc("textstyle");@+break; /*|text_style==2|*/ +case 2: print_esc("scriptstyle");@+break; /*|script_style==4|*/ +case 3: print_esc("scriptscriptstyle");@+break; /*|script_script_style==6|*/ +default:print("Unknown style!"); +} +} + +@ @<Display choice node |p|@>= +{@+print_esc("mathchoice"); +append_char('D');show_node_list(display_mlist(p));flush_char; +append_char('T');show_node_list(text_mlist(p));flush_char; +append_char('S');show_node_list(script_mlist(p));flush_char; +append_char('s');show_node_list(script_script_mlist(p));flush_char; +} + +@ @<Display normal noad |p|@>= +{@+switch (type(p)) { +case ord_noad: print_esc("mathord");@+break; +case op_noad: print_esc("mathop");@+break; +case bin_noad: print_esc("mathbin");@+break; +case rel_noad: print_esc("mathrel");@+break; +case open_noad: print_esc("mathopen");@+break; +case close_noad: print_esc("mathclose");@+break; +case punct_noad: print_esc("mathpunct");@+break; +case inner_noad: print_esc("mathinner");@+break; +case over_noad: print_esc("overline");@+break; +case under_noad: print_esc("underline");@+break; +case vcenter_noad: print_esc("vcenter");@+break; +case radical_noad: {@+print_esc("radical");print_delimiter(left_delimiter(p)); + } @+break; +case accent_noad: {@+print_esc("accent");print_fam_and_char(accent_chr(p)); + } @+break; +case left_noad: {@+print_esc("left");print_delimiter(delimiter(p)); + } @+break; +case right_noad: {@+if (subtype(p)==normal) print_esc("right"); + else print_esc("middle"); + print_delimiter(delimiter(p)); + } +} +if (type(p) < left_noad) + {@+if (subtype(p)!=normal) + if (subtype(p)==limits) print_esc("limits"); + else print_esc("nolimits"); + print_subsidiary_data(nucleus(p),'.'); + } +print_subsidiary_data(supscr(p),'^'); +print_subsidiary_data(subscr(p),'_'); +} + +@ @<Display fraction noad |p|@>= +{@+print_esc("fraction, thickness "); +if (thickness(p)==default_code) print("= default"); +else print_scaled(thickness(p)); +if ((small_fam(left_delimiter(p))!=0)||@+ + (small_char(left_delimiter(p))!=min_quarterword)||@| + (large_fam(left_delimiter(p))!=0)||@| + (large_char(left_delimiter(p))!=min_quarterword)) + {@+print(", left-delimiter ");print_delimiter(left_delimiter(p)); + } +if ((small_fam(right_delimiter(p))!=0)||@| + (small_char(right_delimiter(p))!=min_quarterword)||@| + (large_fam(right_delimiter(p))!=0)||@| + (large_char(right_delimiter(p))!=min_quarterword)) + {@+print(", right-delimiter ");print_delimiter(right_delimiter(p)); + } +print_subsidiary_data(numerator(p),'\\'); +print_subsidiary_data(denominator(p),'/'); +} + +@ That which can be displayed can also be destroyed. + +@<Cases of |flush_node_list| that arise...@>= +case style_node: {@+free_node(p, style_node_size);goto done; + } +case choice_node: {@+flush_node_list(display_mlist(p)); + flush_node_list(text_mlist(p)); + flush_node_list(script_mlist(p)); + flush_node_list(script_script_mlist(p)); + free_node(p, style_node_size);goto done; + } +case ord_noad: case op_noad: case bin_noad: + case rel_noad: case open_noad: case close_noad: + case punct_noad: case inner_noad: + case radical_noad: case over_noad: case under_noad: + case vcenter_noad: case accent_noad: @t@>@;@/ + {@+if (math_type(nucleus(p)) >= sub_box) + flush_node_list(info(nucleus(p))); + if (math_type(supscr(p)) >= sub_box) + flush_node_list(info(supscr(p))); + if (math_type(subscr(p)) >= sub_box) + flush_node_list(info(subscr(p))); + if (type(p)==radical_noad) free_node(p, radical_noad_size); + else if (type(p)==accent_noad) free_node(p, accent_noad_size); + else free_node(p, noad_size); + goto done; + } +case left_noad: case right_noad: {@+free_node(p, noad_size);goto done; + } +case fraction_noad: {@+flush_node_list(info(numerator(p))); + flush_node_list(info(denominator(p))); + free_node(p, fraction_noad_size);goto done; + } + +@* Subroutines for math mode. +In order to convert mlists to hlists, i.e., noads to nodes, we need several +subroutines that are conveniently dealt with now. + +Let us first introduce the macros that make it easy to get at the parameters and +other font information. A size code, which is a multiple of 16, is added to a +family number to get an index into the table of internal font numbers +for each combination of family and size. (Be alert: Size codes get +larger as the type gets smaller.) + +@d text_size 0 /*size code for the largest size in a family*/ +@d script_size 16 /*size code for the medium size in a family*/ +@d script_script_size 32 /*size code for the smallest size in a family*/ + +@<Basic printing procedures@>= +static void print_size(int @!s) +{@+if (s==text_size) print_esc("textfont"); +else if (s==script_size) print_esc("scriptfont"); +else print_esc("scriptscriptfont"); +} + +@ Before an mlist is converted to an hlist, \TeX\ makes sure that +the fonts in family~2 have enough parameters to be math-symbol +fonts, and that the fonts in family~3 have enough parameters to be +math-extension fonts. The math-symbol parameters are referred to by using the +following macros, which take a size code as their parameter; for example, +|num1(cur_size)| gives the value of the |num1| parameter for the current size. +@^parameters for symbols@> +@^font parameters@> + +@d mathsy_end(A) fam_fnt(2+A)]].sc +@d mathsy(A) font_info[A+param_base[mathsy_end +@d math_x_height mathsy(5) /*height of `\.x'*/ +@d math_quad mathsy(6) /*\.{18mu}*/ +@d num1 mathsy(8) /*numerator shift-up in display styles*/ +@d num2 mathsy(9) /*numerator shift-up in non-display, non-\.{\\atop}*/ +@d num3 mathsy(10) /*numerator shift-up in non-display \.{\\atop}*/ +@d denom1 mathsy(11) /*denominator shift-down in display styles*/ +@d denom2 mathsy(12) /*denominator shift-down in non-display styles*/ +@d sup1 mathsy(13) /*superscript shift-up in uncramped display style*/ +@d sup2 mathsy(14) /*superscript shift-up in uncramped non-display*/ +@d sup3 mathsy(15) /*superscript shift-up in cramped styles*/ +@d sub1 mathsy(16) /*subscript shift-down if superscript is absent*/ +@d sub2 mathsy(17) /*subscript shift-down if superscript is present*/ +@d sup_drop mathsy(18) /*superscript baseline below top of large box*/ +@d sub_drop mathsy(19) /*subscript baseline below bottom of large box*/ +@d delim1 mathsy(20) /*size of \.{\\atopwithdelims} delimiters + in display styles*/ +@d delim2 mathsy(21) /*size of \.{\\atopwithdelims} delimiters in non-displays*/ +@d axis_height mathsy(22) /*height of fraction lines above the baseline*/ +@d total_mathsy_params 22 + +@ The math-extension parameters have similar macros, but the size code is +omitted (since it is always |cur_size| when we refer to such parameters). +@^parameters for symbols@> +@^font parameters@> + +@d mathex(A) font_info[A+param_base[fam_fnt(3+cur_size)]].sc +@d default_rule_thickness mathex(8) /*thickness of \.{\\over} bars*/ +@d big_op_spacing1 mathex(9) /*minimum clearance above a displayed op*/ +@d big_op_spacing2 mathex(10) /*minimum clearance below a displayed op*/ +@d big_op_spacing3 mathex(11) /*minimum baselineskip above displayed op*/ +@d big_op_spacing4 mathex(12) /*minimum baselineskip below displayed op*/ +@d big_op_spacing5 mathex(13) /*padding above and below displayed limits*/ +@d total_mathex_params 13 + +@ We also need to compute the change in style between mlists and their +subsidiaries. The following macros define the subsidiary style for +an overlined nucleus (|cramped_style|), for a subscript or a superscript +(|sub_style| or |sup_style|), or for a numerator or denominator (|num_style| +or |denom_style|). + +@d cramped_style(A) 2*(A/2)+cramped /*cramp the style*/ +@d sub_style(A) 2*(A/4)+script_style+cramped /*smaller and cramped*/ +@d sup_style(A) 2*(A/4)+script_style+(A%2) /*smaller*/ +@d num_style(A) A+2-2*(A/6) /*smaller unless already script-script*/ +@d denom_style(A) 2*(A/2)+cramped+2-2*(A/6) /*smaller, cramped*/ + +@ When the style changes, the following piece of program computes associated +information: + +@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>= +{@+if (cur_style < script_style) cur_size=text_size; +else cur_size=16*((cur_style-text_style)/2); +cur_mu=x_over_n(math_quad(cur_size), 18); +} + +@ Here is a function that returns a pointer to a rule node having a given +thickness |t|. The rule will extend horizontally to the boundary of the vlist +that eventually contains it. + +@p static pointer fraction_rule(scaled @!t) + /*construct the bar for a fraction*/ +{@+pointer p; /*the new node*/ +p=new_rule();height(p)=t;depth(p)=0;return p; +} + +@ The |overbar| function returns a pointer to a vlist box that consists of +a given box |b|, above which has been placed a kern of height |k| under a +fraction rule of thickness |t| under additional space of height |t|. + +@p static pointer overbar(pointer @!b, scaled @!k, scaled @!t) +{@+pointer p, @!q; /*nodes being constructed*/ +p=new_kern(k);link(p)=b;q=fraction_rule(t);link(q)=p; +p=new_kern(t);link(p)=q;return vpack(p, natural); +} + +@ The |var_delimiter| function, which finds or constructs a sufficiently +large delimiter, is the most interesting of the auxiliary functions that +currently concern us. Given a pointer |d| to a delimiter field in some noad, +together with a size code |s| and a vertical distance |v|, this function +returns a pointer to a box that contains the smallest variant of |d| whose +height plus depth is |v| or more. (And if no variant is large enough, it +returns the largest available variant.) In particular, this routine will +construct arbitrarily large delimiters from extensible components, if +|d| leads to such characters. + +The value returned is a box whose |shift_amount| has been set so that +the box is vertically centered with respect to the axis in the given size. +If a built-up symbol is returned, the height of the box before shifting +will be the height of its topmost component. + +@p @t\4@>@<Declare subprocedures for |var_delimiter|@>@; +static pointer var_delimiter(pointer @!d, small_number @!s, scaled @!v) +{@+ +pointer b; /*the box that will be constructed*/ +internal_font_number @!f, @!g; /*best-so-far and tentative font codes*/ +quarterword @!c, @!x, @!y; /*best-so-far and tentative character codes*/ +int @!m, @!n; /*the number of extensible pieces*/ +scaled @!u; /*height-plus-depth of a tentative character*/ +scaled @!w; /*largest height-plus-depth so far*/ +four_quarters @!q; /*character info*/ +eight_bits @!hd; /*height-depth byte*/ +four_quarters @!r; /*extensible pieces*/ +small_number @!z; /*runs through font family members*/ +bool @!large_attempt; /*are we trying the ``large'' variant?*/ +f=null_font;w=0;large_attempt=false; +z=small_fam(d);x=small_char(d); +loop@+{@+@<Look at the variants of |(z,x)|; set |f| and |c| whenever a better +character is found; |goto found| as soon as a large enough variant is encountered@>; + if (large_attempt) goto found; /*there were none large enough*/ + large_attempt=true;z=large_fam(d);x=large_char(d); + } +found: if (f!=null_font) + @<Make variable |b| point to a box for |(f,c)|@>; +else{@+b=new_null_box(); + width(b)=null_delimiter_space; /*use this width if no delimiter was found*/ + } +shift_amount(b)=half(height(b)-depth(b))-axis_height(s); +return b; +} + +@ The search process is complicated slightly by the facts that some of the +characters might not be present in some of the fonts, and they might not +be probed in increasing order of height. + +@<Look at the variants of |(z,x)|; set |f| and |c|...@>= +if ((z!=0)||(x!=min_quarterword)) + {@+z=z+s+16; + @/do@+{z=z-16;g=fam_fnt(z); + if (g!=null_font) + @<Look at the list of characters starting with |x| in font |g|; set |f| +and |c| whenever a better character is found; |goto found| as soon as a large +enough variant is encountered@>; + }@+ while (!(z < 16)); + } + +@ @<Look at the list of characters starting with |x|...@>= +{@+y=x; +if ((qo(y) >= font_bc[g])&&(qo(y) <= font_ec[g])) + {@+resume: q=char_info(g, y); + if (char_exists(q)) + {@+if (char_tag(q)==ext_tag) + {@+f=g;c=y;goto found; + } + hd=height_depth(q); + u=char_height(g, hd)+char_depth(g, hd); + if (u > w) + {@+f=g;c=y;w=u; + if (u >= v) goto found; + } + if (char_tag(q)==list_tag) + {@+y=rem_byte(q);goto resume; + } + } + } +} + +@ Here is a subroutine that creates a new box, whose list contains a +single character, and whose width includes the italic correction for +that character. The height or depth of the box will be negative, if +the height or depth of the character is negative; thus, this routine +may deliver a slightly different result than |hpack| would produce. + +@<Declare subprocedures for |var_delimiter|@>= +static pointer char_box(internal_font_number @!f, quarterword @!c) +{@+four_quarters q; +eight_bits @!hd; /*|height_depth| byte*/ +pointer @!b, @!p; /*the new box and its character node*/ +q=char_info(f, c);hd=height_depth(q); +b=new_null_box();width(b)=char_width(f, q)+char_italic(f, q); +height(b)=char_height(f, hd);depth(b)=char_depth(f, hd); +p=get_avail();character(p)=c;font(p)=f;list_ptr(b)=p;return b; +} + +@ When the following code is executed, |char_tag(q)| will be equal to +|ext_tag| if and only if a built-up symbol is supposed to be returned. + +@<Make variable |b| point to a box for |(f,c)|@>= +if (char_tag(q)==ext_tag) + @<Construct an extensible character in a new box |b|, using recipe |rem_byte(q)| +and font |f|@>@; +else b=char_box(f, c) + +@ When we build an extensible character, it's handy to have the +following subroutine, which puts a given character on top +of the characters already in box |b|: + +@<Declare subprocedures for |var_delimiter|@>= +static void stack_into_box(pointer @!b, internal_font_number @!f, + quarterword @!c) +{@+pointer p; /*new node placed into |b|*/ +p=char_box(f, c);link(p)=list_ptr(b);list_ptr(b)=p; +height(b)=height(p); +} + +@ Another handy subroutine computes the height plus depth of +a given character: + +@<Declare subprocedures for |var_delimiter|@>= +static scaled height_plus_depth(internal_font_number @!f, quarterword @!c) +{@+four_quarters q; +eight_bits @!hd; /*|height_depth| byte*/ +q=char_info(f, c);hd=height_depth(q); +return char_height(f, hd)+char_depth(f, hd); +} + +@ @<Construct an extensible...@>= +{@+b=new_null_box(); +type(b)=vlist_node; +r=font_info[exten_base[f]+rem_byte(q)].qqqq;@/ +@<Compute the minimum suitable height, |w|, and the corresponding number of +extension steps, |n|; also set |width(b)|@>; +c=ext_bot(r); +if (c!=min_quarterword) stack_into_box(b, f, c); +c=ext_rep(r); +for (m=1; m<=n; m++) stack_into_box(b, f, c); +c=ext_mid(r); +if (c!=min_quarterword) + {@+stack_into_box(b, f, c);c=ext_rep(r); + for (m=1; m<=n; m++) stack_into_box(b, f, c); + } +c=ext_top(r); +if (c!=min_quarterword) stack_into_box(b, f, c); +depth(b)=w-height(b); +} + +@ The width of an extensible character is the width of the repeatable +module. If this module does not have positive height plus depth, +we don't use any copies of it, otherwise we use as few as possible +(in groups of two if there is a middle part). + +@<Compute the minimum suitable height, |w|, and...@>= +c=ext_rep(r);u=height_plus_depth(f, c); +w=0;q=char_info(f, c);width(b)=char_width(f, q)+char_italic(f, q);@/ +c=ext_bot(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c); +c=ext_mid(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c); +c=ext_top(r);@+if (c!=min_quarterword) w=w+height_plus_depth(f, c); +n=0; +if (u > 0) while (w < v) + {@+w=w+u;incr(n); + if (ext_mid(r)!=min_quarterword) w=w+u; + } + +@ The next subroutine is much simpler; it is used for numerators and +denominators of fractions as well as for displayed operators and +their limits above and below. It takes a given box~|b| and +changes it so that the new box is centered in a box of width~|w|. +The centering is done by putting \.{\\hss} glue at the left and right +of the list inside |b|, then packaging the new box; thus, the +actual box might not really be centered, if it already contains +infinite glue. + +The given box might contain a single character whose italic correction +has been added to the width of the box; in this case a compensating +kern is inserted. + +@p static pointer rebox(pointer @!b, scaled @!w) +{@+pointer p; /*temporary register for list manipulation*/ +internal_font_number @!f; /*font in a one-character box*/ +scaled @!v; /*width of a character without italic correction*/ +if ((width(b)!=w)&&(list_ptr(b)!=null)) + {@+if (type(b)==vlist_node) b=hpack(b, natural); + p=list_ptr(b); + if ((is_char_node(p))&&(link(p)==null)) + {@+f=font(p);v=char_width(f, char_info(f, character(p))); + if (v!=width(b)) link(p)=new_kern(width(b)-v); + } + free_node(b, box_node_size); + b=new_glue(ss_glue);link(b)=p; + while (link(p)!=null) p=link(p); + link(p)=new_glue(ss_glue); + return hpack(b, w, exactly); + } +else{@+width(b)=w;return b; + } +} + +@ Here is a subroutine that creates a new glue specification from another +one that is expressed in `\.{mu}', given the value of the math unit. + +@d mu_mult(A) nx_plus_y(n, A, xn_over_d(A, f, 0200000)) + +@p static pointer math_glue(pointer @!g, scaled @!m) +{@+pointer p; /*the new glue specification*/ +int @!n; /*integer part of |m|*/ +scaled @!f; /*fraction part of |m|*/ +n=x_over_n(m, 0200000);f=rem;@/ +if (f < 0) + {@+decr(n);f=f+0200000; + } +p=get_node(glue_spec_size); +width(p)=mu_mult(width(g)); /*convert \.{mu} to \.{pt}*/ +stretch_order(p)=stretch_order(g); +if (stretch_order(p)==normal) stretch(p)=mu_mult(stretch(g)); +else stretch(p)=stretch(g); +shrink_order(p)=shrink_order(g); +if (shrink_order(p)==normal) shrink(p)=mu_mult(shrink(g)); +else shrink(p)=shrink(g); +return p; +} + +@ The |math_kern| subroutine removes |mu_glue| from a kern node, given +the value of the math unit. + +@p static void math_kern(pointer @!p, scaled @!m) +{@+int @!n; /*integer part of |m|*/ +scaled @!f; /*fraction part of |m|*/ +if (subtype(p)==mu_glue) + {@+n=x_over_n(m, 0200000);f=rem;@/ + if (f < 0) + {@+decr(n);f=f+0200000; + } + width(p)=mu_mult(width(p));subtype(p)=explicit; + } +} + +@ Sometimes it is necessary to destroy an mlist. The following +subroutine empties the current list, assuming that |abs(mode)==mmode|. + +@p static void flush_math(void) +{@+flush_node_list(link(head));flush_node_list(incompleat_noad); +link(head)=null;tail=head;incompleat_noad=null; +} + +@* Typesetting math formulas. +\TeX's most important routine for dealing with formulas is called +|mlist_to_hlist|. After a formula has been scanned and represented as an +mlist, this routine converts it to an hlist that can be placed into a box +or incorporated into the text of a paragraph. There are three implicit +parameters, passed in global variables: |cur_mlist| points to the first +node or noad in the given mlist (and it might be |null|); |cur_style| is a +style code; and |mlist_penalties| is |true| if penalty nodes for potential +line breaks are to be inserted into the resulting hlist. After +|mlist_to_hlist| has acted, |link(temp_head)| points to the translated hlist. + +Since mlists can be inside mlists, the procedure is recursive. And since this +is not part of \TeX's inner loop, the program has been written in a manner +that stresses compactness over efficiency. +@^recursion@> + +@<Glob...@>= +static pointer @!cur_mlist; /*beginning of mlist to be translated*/ +static small_number @!cur_style; /*style code at current place in the list*/ +static small_number @!cur_size; /*size code corresponding to |cur_style|*/ +static scaled @!cur_mu; /*the math unit width corresponding to |cur_size|*/ +static bool @!mlist_penalties; /*should |mlist_to_hlist| insert penalties?*/ + +@ The recursion in |mlist_to_hlist| is due primarily to a subroutine +called |clean_box| that puts a given noad field into a box using a given +math style; |mlist_to_hlist| can call |clean_box|, which can call +|mlist_to_hlist|. +@^recursion@> + +The box returned by |clean_box| is ``clean'' in the +sense that its |shift_amount| is zero. + +@p static void mlist_to_hlist(void);@/ +static pointer clean_box(pointer @!p, small_number @!s) +{@+ +pointer q; /*beginning of a list to be boxed*/ +small_number @!save_style; /*|cur_style| to be restored*/ +pointer @!x; /*box to be returned*/ +pointer @!r; /*temporary pointer*/ +switch (math_type(p)) { +case math_char: {@+cur_mlist=new_noad();mem[nucleus(cur_mlist)]=mem[p]; + } @+break; +case sub_box: {@+q=info(p);goto found; + } +case sub_mlist: cur_mlist=info(p);@+break; +default:{@+q=new_null_box();goto found; + } +} @/ +save_style=cur_style;cur_style=s;mlist_penalties=false;@/ +mlist_to_hlist();q=link(temp_head); /*recursive call*/ +cur_style=save_style; /*restore the style*/ +@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; +found: if (is_char_node(q)||(q==null)) x=hpack(q, natural); + else if ((link(q)==null)&&(type(q) <= vlist_node)&&(shift_amount(q)==0)) + x=q; /*it's already clean*/ + else x=hpack(q, natural); +@<Simplify a trivial box@>; +return x; +} + +@ Here we save memory space in a common case. + +@<Simplify a trivial box@>= +q=list_ptr(x); +if (is_char_node(q)) + {@+r=link(q); + if (r!=null) if (link(r)==null) if (!is_char_node(r)) + if (type(r)==kern_node) /*unneeded italic correction*/ + {@+free_node(r, small_node_size);link(q)=null; + } + } + +@ It is convenient to have a procedure that converts a |math_char| +field to an ``unpacked'' form. The |fetch| routine sets |cur_f|, |cur_c|, +and |cur_i| to the font code, character code, and character information bytes of +a given noad field. It also takes care of issuing error messages for +nonexistent characters; in such cases, |char_exists(cur_i)| will be |false| +after |fetch| has acted, and the field will also have been reset to |empty|. + +@p static void fetch(pointer @!a) /*unpack the |math_char| field |a|*/ +{@+cur_c=character(a);cur_f=fam_fnt(fam(a)+cur_size); +if (cur_f==null_font) + @<Complain about an undefined family and set |cur_i| null@>@; +else{@+if ((qo(cur_c) >= font_bc[cur_f])&&(qo(cur_c) <= font_ec[cur_f])) + cur_i=char_info(cur_f, cur_c); + else cur_i=null_character; + if (!(char_exists(cur_i))) + {@+char_warning(cur_f, qo(cur_c)); + math_type(a)=empty;cur_i=null_character; + } + } +} + +@ @<Complain about an undefined family...@>= +{@+print_err("");print_size(cur_size);print_char(' '); +print_int(fam(a));print(" is undefined (character "); +print_ASCII(qo(cur_c));print_char(')'); +help4("Somewhere in the math formula just ended, you used the",@/ +"stated character from an undefined font family. For example,",@/ +"plain TeX doesn't allow \\it or \\sl in subscripts. Proceed,",@/ +"and I'll try to forget that I needed that character."); +error();cur_i=null_character;math_type(a)=empty; +} + +@ The outputs of |fetch| are placed in global variables. + +@<Glob...@>= +static internal_font_number @!cur_f; /*the |font| field of a |math_char|*/ +static quarterword @!cur_c; /*the |character| field of a |math_char|*/ +static four_quarters @!cur_i; /*the |char_info| of a |math_char|, + or a lig/kern instruction*/ + +@ We need to do a lot of different things, so |mlist_to_hlist| makes two +passes over the given mlist. + +The first pass does most of the processing: It removes ``mu'' spacing from +glue, it recursively evaluates all subsidiary mlists so that only the +top-level mlist remains to be handled, it puts fractions and square roots +and such things into boxes, it attaches subscripts and superscripts, and +it computes the overall height and depth of the top-level mlist so that +the size of delimiters for a |left_noad| and a |right_noad| will be known. +The hlist resulting from each noad is recorded in that noad's |new_hlist| +field, an integer field that replaces the |nucleus| or |thickness|. +@^recursion@> + +The second pass eliminates all noads and inserts the correct glue and +penalties between nodes. + +@d new_hlist(A) mem[nucleus(A)].i /*the translation of an mlist*/ + +@ Here is the overall plan of |mlist_to_hlist|, and the list of its +local variables. + +@p @t\4@>@<Declare math construction procedures@>@; +static void mlist_to_hlist(void) +{@+ + +pointer mlist; /*beginning of the given list*/ +bool @!penalties; /*should penalty nodes be inserted?*/ +small_number @!style; /*the given style*/ +small_number @!save_style; /*holds |cur_style| during recursion*/ +pointer @!q; /*runs through the mlist*/ +pointer @!r; /*the most recent noad preceding |q|*/ +small_number @!r_type; /*the |type| of noad |r|, or |op_noad| if |r==null|*/ +small_number @!t; /*the effective |type| of noad |q| during the second pass*/ +pointer @!p, @!x, @!y, @!z; /*temporary registers for list construction*/ +int @!pen; /*a penalty to be inserted*/ +small_number @!s; /*the size of a noad to be deleted*/ +scaled @!max_h, @!max_d; /*maximum height and depth of the list translated so far*/ +scaled @!delta; /*offset between subscript and superscript*/ +mlist=cur_mlist;penalties=mlist_penalties; +style=cur_style; /*tuck global parameters away as local variables*/ +q=mlist;r=null;r_type=op_noad;max_h=0;max_d=0; +@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; +while (q!=null) @<Process node-or-noad |q| as much as possible in preparation +for the second pass of |mlist_to_hlist|, then move to the next item in the +mlist@>; +@<Convert \(a)a final |bin_noad| to an |ord_noad|@>; +@<Make a second pass over the mlist, removing all noads and inserting the +proper spacing and penalties@>; +} + +@ We use the fact that no character nodes appear in an mlist, hence +the field |type(q)| is always present. + +@<Process node-or-noad...@>= +{@+@<Do first-pass processing based on |type(q)|; |goto done_with_noad| if +a noad has been fully processed, |goto check_dimensions| if it has been translated +into |new_hlist(q)|, or |goto done_with_node| if a node has been fully processed@>; +check_dimensions: z=hpack(new_hlist(q), natural); +if (height(z) > max_h) max_h=height(z); +if (depth(z) > max_d) max_d=depth(z); +free_node(z, box_node_size); +done_with_noad: r=q;r_type=type(r); +if (r_type==right_noad) + {@+r_type=left_noad;cur_style=style;@<Set up the values...@>; + } +done_with_node: q=link(q); +} + +@ One of the things we must do on the first pass is change a |bin_noad| to +an |ord_noad| if the |bin_noad| is not in the context of a binary operator. +The values of |r| and |r_type| make this fairly easy. + +@<Do first-pass processing...@>= +reswitch: delta=0; +switch (type(q)) { +case bin_noad: switch (r_type) { + case bin_noad: case op_noad: case rel_noad: + case open_noad: case punct_noad: case left_noad: + {@+type(q)=ord_noad;goto reswitch; + } + default:do_nothing; + } @+break; +case rel_noad: case close_noad: case punct_noad: + case right_noad: {@+@t@>@;@/ + @<Convert \(a)a final |bin_noad| to an |ord_noad|@>; + if (type(q)==right_noad) goto done_with_noad; + } @+break; +@t\4@>@<Cases for noads that can follow a |bin_noad|@>@; +@t\4@>@<Cases for nodes that can appear in an mlist, after which we |goto +done_with_node|@>@; +default:confusion("mlist1"); +@:this can't happen mlist1}{\quad mlist1@> +} @/ +@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>@; + +@ @<Convert \(a)a final |bin_noad| to an |ord_noad|@>= +if (r_type==bin_noad) type(r)=ord_noad + +@ @<Cases for nodes that can appear in an mlist...@>= +case style_node: {@+cur_style=subtype(q); + @<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; + goto done_with_node; + } +case choice_node: @<Change this node to a style node followed by the correct +choice, then |goto done_with_node|@>@; +case ins_node: case mark_node: case adjust_node: + case whatsit_node: case penalty_node: + case disc_node: goto done_with_node; +case rule_node: {@+if (height(q) > max_h) max_h=height(q); + if (depth(q) > max_d) max_d=depth(q);goto done_with_node; + } +case glue_node: {@+@<Convert \(m)math glue to ordinary glue@>; + goto done_with_node; + } +case kern_node: {@+math_kern(q, cur_mu);goto done_with_node; + } + +@ @d choose_mlist(A) {@+p=A(q);A(q)=null;@+} + +@<Change this node to a style node...@>= +{@+switch (cur_style/2) { +case 0: choose_mlist(display_mlist)@;@+break; /*|display_style==0|*/ +case 1: choose_mlist(text_mlist)@;@+break; /*|text_style==2|*/ +case 2: choose_mlist(script_mlist)@;@+break; /*|script_style==4|*/ +case 3: choose_mlist(script_script_mlist); /*|script_script_style==6|*/ +} /*there are no other cases*/ +flush_node_list(display_mlist(q)); +flush_node_list(text_mlist(q)); +flush_node_list(script_mlist(q)); +flush_node_list(script_script_mlist(q));@/ +type(q)=style_node;subtype(q)=cur_style;width(q)=0;depth(q)=0; +if (p!=null) + {@+z=link(q);link(q)=p; + while (link(p)!=null) p=link(p); + link(p)=z; + } +goto done_with_node; +} + +@ Conditional math glue (`\.{\\nonscript}') results in a |glue_node| +pointing to |zero_glue|, with |subtype(q)==cond_math_glue|; in such a case +the node following will be eliminated if it is a glue or kern node and if the +current size is different from |text_size|. Unconditional math glue +(`\.{\\muskip}') is converted to normal glue by multiplying the dimensions +by |cur_mu|. +@!@:non\_script\_}{\.{\\nonscript} primitive@> + +@<Convert \(m)math glue to ordinary glue@>= +if (subtype(q)==mu_glue) + {@+x=glue_ptr(q); + y=math_glue(x, cur_mu);delete_glue_ref(x);glue_ptr(q)=y; + subtype(q)=normal; + } +else if ((cur_size!=text_size)&&(subtype(q)==cond_math_glue)) + {@+p=link(q); + if (p!=null) if ((type(p)==glue_node)||(type(p)==kern_node)) + {@+link(q)=link(p);link(p)=null;flush_node_list(p); + } + } + +@ @<Cases for noads that can follow a |bin_noad|@>= +case left_noad: goto done_with_noad; +case fraction_noad: {@+make_fraction(q);goto check_dimensions; + } +case op_noad: {@+delta=make_op(q); + if (subtype(q)==limits) goto check_dimensions; + } @+break; +case ord_noad: make_ord(q);@+break; +case open_noad: case inner_noad: do_nothing;@+break; +case radical_noad: make_radical(q);@+break; +case over_noad: make_over(q);@+break; +case under_noad: make_under(q);@+break; +case accent_noad: make_math_accent(q);@+break; +case vcenter_noad: make_vcenter(q);@+break; + +@ Most of the actual construction work of |mlist_to_hlist| is done +by procedures with names +like |make_fraction|, |make_radical|, etc. To illustrate +the general setup of such procedures, let's begin with a couple of +simple ones. + +@<Declare math...@>= +static void make_over(pointer @!q) +{@+info(nucleus(q))=@| + overbar(clean_box(nucleus(q), cramped_style(cur_style)),@| + 3*default_rule_thickness, default_rule_thickness); +math_type(nucleus(q))=sub_box; +} + +@ @<Declare math...@>= +static void make_under(pointer @!q) +{@+pointer p, @!x, @!y; /*temporary registers for box construction*/ +scaled @!delta; /*overall height plus depth*/ +x=clean_box(nucleus(q), cur_style); +p=new_kern(3*default_rule_thickness);link(x)=p; +link(p)=fraction_rule(default_rule_thickness); +y=vpack(x, natural); +delta=height(y)+depth(y)+default_rule_thickness; +height(y)=height(x);depth(y)=delta-height(y); +info(nucleus(q))=y;math_type(nucleus(q))=sub_box; +} + +@ @<Declare math...@>= +static void make_vcenter(pointer @!q) +{@+pointer v; /*the box that should be centered vertically*/ +scaled @!delta; /*its height plus depth*/ +v=info(nucleus(q)); +if (type(v)!=vlist_node) confusion("vcenter"); +@:this can't happen vcenter}{\quad vcenter@> +delta=height(v)+depth(v); +height(v)=axis_height(cur_size)+half(delta); +depth(v)=delta-height(v); +} + +@ According to the rules in the \.{DVI} file specifications, we ensure alignment +@^square roots@> +between a square root sign and the rule above its nucleus by assuming that the +baseline of the square-root symbol is the same as the bottom of the rule. The +height of the square-root symbol will be the thickness of the rule, and the +depth of the square-root symbol should exceed or equal the height-plus-depth +of the nucleus plus a certain minimum clearance~|clr|. The symbol will be +placed so that the actual clearance is |clr| plus half the excess. + +@<Declare math...@>= +static void make_radical(pointer @!q) +{@+pointer x, @!y; /*temporary registers for box construction*/ +scaled @!delta, @!clr; /*dimensions involved in the calculation*/ +x=clean_box(nucleus(q), cramped_style(cur_style)); +if (cur_style < text_style) /*display style*/ + clr=default_rule_thickness+(abs(math_x_height(cur_size))/4); +else{@+clr=default_rule_thickness;clr=clr+(abs(clr)/4); + } +y=var_delimiter(left_delimiter(q), cur_size, height(x)+depth(x)+clr+ + default_rule_thickness); +delta=depth(y)-(height(x)+depth(x)+clr); +if (delta > 0) clr=clr+half(delta); /*increase the actual clearance*/ +shift_amount(y)=-(height(x)+clr); +link(y)=overbar(x, clr, height(y)); +info(nucleus(q))=hpack(y, natural);math_type(nucleus(q))=sub_box; +} + +@ Slants are not considered when placing accents in math mode. The accenter is +centered over the accentee, and the accent width is treated as zero with +respect to the size of the final box. + +@<Declare math...@>= +static void make_math_accent(pointer @!q) +{@+ +pointer p, @!x, @!y; /*temporary registers for box construction*/ +int @!a; /*address of lig/kern instruction*/ +quarterword @!c; /*accent character*/ +internal_font_number @!f; /*its font*/ +four_quarters @!i; /*its |char_info|*/ +scaled @!s; /*amount to skew the accent to the right*/ +scaled @!h; /*height of character being accented*/ +scaled @!delta; /*space to remove between accent and accentee*/ +scaled @!w; /*width of the accentee, not including sub/superscripts*/ +fetch(accent_chr(q)); +if (char_exists(cur_i)) + {@+i=cur_i;c=cur_c;f=cur_f;@/ + @<Compute the amount of skew@>; + x=clean_box(nucleus(q), cramped_style(cur_style));w=width(x);h=height(x); + @<Switch to a larger accent if available and appropriate@>; + if (h < x_height(f)) delta=h;@+else delta=x_height(f); + if ((math_type(supscr(q))!=empty)||(math_type(subscr(q))!=empty)) + if (math_type(nucleus(q))==math_char) + @<Swap the subscript and superscript into box |x|@>; + y=char_box(f, c); + shift_amount(y)=s+half(w-width(y)); + width(y)=0;p=new_kern(-delta);link(p)=x;link(y)=p; + y=vpack(y, natural);width(y)=width(x); + if (height(y) < h) @<Make the height of box |y| equal to |h|@>; + info(nucleus(q))=y; + math_type(nucleus(q))=sub_box; + } +} + +@ @<Make the height of box |y|...@>= +{@+p=new_kern(h-height(y));link(p)=list_ptr(y);list_ptr(y)=p; +height(y)=h; +} + +@ @<Switch to a larger accent if available and appropriate@>= +loop@+{@+if (char_tag(i)!=list_tag) goto done; + y=rem_byte(i); + i=char_info(f, y); + if (!char_exists(i)) goto done; + if (char_width(f, i) > w) goto done; + c=y; + } +done: + +@ @<Compute the amount of skew@>= +s=0; +if (math_type(nucleus(q))==math_char) + {@+fetch(nucleus(q)); + if (char_tag(cur_i)==lig_tag) + {@+a=lig_kern_start(cur_f, cur_i); + cur_i=font_info[a].qqqq; + if (skip_byte(cur_i) > stop_flag) + {@+a=lig_kern_restart(cur_f, cur_i); + cur_i=font_info[a].qqqq; + } + loop@+{@+if (qo(next_char(cur_i))==skew_char[cur_f]) + {@+if (op_byte(cur_i) >= kern_flag) + if (skip_byte(cur_i) <= stop_flag) s=char_kern(cur_f, cur_i); + goto done1; + } + if (skip_byte(cur_i) >= stop_flag) goto done1; + a=a+qo(skip_byte(cur_i))+1; + cur_i=font_info[a].qqqq; + } + } + } +done1: + +@ @<Swap the subscript and superscript into box |x|@>= +{@+flush_node_list(x);x=new_noad(); +mem[nucleus(x)]=mem[nucleus(q)]; +mem[supscr(x)]=mem[supscr(q)]; +mem[subscr(x)]=mem[subscr(q)];@/ +mem[supscr(q)].hh=empty_field; +mem[subscr(q)].hh=empty_field;@/ +math_type(nucleus(q))=sub_mlist;info(nucleus(q))=x; +x=clean_box(nucleus(q), cur_style);delta=delta+height(x)-h;h=height(x); +} + +@ The |make_fraction| procedure is a bit different because it sets +|new_hlist(q)| directly rather than making a sub-box. + +@<Declare math...@>= +static void make_fraction(pointer @!q) +{@+pointer p, @!v, @!x, @!y, @!z; /*temporary registers for box construction*/ +scaled @!delta, @!delta1, @!delta2, @!shift_up, @!shift_down, @!clr; + /*dimensions for box calculations*/ +if (thickness(q)==default_code) thickness(q)=default_rule_thickness; +@<Create equal-width boxes |x| and |z| for the numerator and denominator, +and compute the default amounts |shift_up| and |shift_down| by which they +are displaced from the baseline@>; +if (thickness(q)==0) @<Adjust \(s)|shift_up| and |shift_down| for the case +of no fraction line@>@; +else@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>; +@<Construct a vlist box for the fraction, according to |shift_up| and |shift_down|@>; +@<Put the \(f)fraction into a box with its delimiters, and make |new_hlist(q)| +point to it@>; +} + +@ @<Create equal-width boxes |x| and |z| for the numerator and denom...@>= +x=clean_box(numerator(q), num_style(cur_style)); +z=clean_box(denominator(q), denom_style(cur_style)); +if (width(x) < width(z)) x=rebox(x, width(z)); +else z=rebox(z, width(x)); +if (cur_style < text_style) /*display style*/ + {@+shift_up=num1(cur_size);shift_down=denom1(cur_size); + } +else{@+shift_down=denom2(cur_size); + if (thickness(q)!=0) shift_up=num2(cur_size); + else shift_up=num3(cur_size); + } + +@ The numerator and denominator must be separated by a certain minimum +clearance, called |clr| in the following program. The difference between +|clr| and the actual clearance is twice |delta|. + +@<Adjust \(s)|shift_up| and |shift_down| for the case of no fraction line@>= +{@+if (cur_style < text_style) clr=7*default_rule_thickness; +else clr=3*default_rule_thickness; +delta=half(clr-((shift_up-depth(x))-(height(z)-shift_down))); +if (delta > 0) + {@+shift_up=shift_up+delta; + shift_down=shift_down+delta; + } +} + +@ In the case of a fraction line, the minimum clearance depends on the actual +thickness of the line. + +@<Adjust \(s)|shift_up| and |shift_down| for the case of a fraction line@>= +{@+if (cur_style < text_style) clr=3*thickness(q); +else clr=thickness(q); +delta=half(thickness(q)); +delta1=clr-((shift_up-depth(x))-(axis_height(cur_size)+delta)); +delta2=clr-((axis_height(cur_size)-delta)-(height(z)-shift_down)); +if (delta1 > 0) shift_up=shift_up+delta1; +if (delta2 > 0) shift_down=shift_down+delta2; +} + +@ @<Construct a vlist box for the fraction...@>= +v=new_null_box();type(v)=vlist_node; +height(v)=shift_up+height(x);depth(v)=depth(z)+shift_down; +width(v)=width(x); /*this also equals |width(z)|*/ +if (thickness(q)==0) + {@+p=new_kern((shift_up-depth(x))-(height(z)-shift_down)); + link(p)=z; + } +else{@+y=fraction_rule(thickness(q));@/ + p=new_kern((axis_height(cur_size)-delta)-@|(height(z)-shift_down));@/ + link(y)=p;link(p)=z;@/ + p=new_kern((shift_up-depth(x))-(axis_height(cur_size)+delta)); + link(p)=y; + } +link(x)=p;list_ptr(v)=x + +@ @<Put the \(f)fraction into a box with its delimiters...@>= +if (cur_style < text_style) delta=delim1(cur_size); +else delta=delim2(cur_size); +x=var_delimiter(left_delimiter(q), cur_size, delta);link(x)=v;@/ +z=var_delimiter(right_delimiter(q), cur_size, delta);link(v)=z;@/ +new_hlist(q)=hpack(x, natural) + +@ If the nucleus of an |op_noad| is a single character, it is to be +centered vertically with respect to the axis, after first being enlarged +(via a character list in the font) if we are in display style. The normal +convention for placing displayed limits is to put them above and below the +operator in display style. + +The italic correction is removed from the character if there is a subscript +and the limits are not being displayed. The |make_op| +routine returns the value that should be used as an offset between +subscript and superscript. + +After |make_op| has acted, |subtype(q)| will be |limits| if and only if +the limits have been set above and below the operator. In that case, +|new_hlist(q)| will already contain the desired final box. + +@<Declare math...@>= +static scaled make_op(pointer @!q) +{@+scaled delta; /*offset between subscript and superscript*/ +pointer @!p, @!v, @!x, @!y, @!z; /*temporary registers for box construction*/ +quarterword @!c;@+four_quarters @!i; /*registers for character examination*/ +scaled @!shift_up, @!shift_down; /*dimensions for box calculation*/ +if ((subtype(q)==normal)&&(cur_style < text_style)) + subtype(q)=limits; +if (math_type(nucleus(q))==math_char) + {@+fetch(nucleus(q)); + if ((cur_style < text_style)&&(char_tag(cur_i)==list_tag)) /*make it larger*/ + {@+c=rem_byte(cur_i);i=char_info(cur_f, c); + if (char_exists(i)) + {@+cur_c=c;cur_i=i;character(nucleus(q))=c; + } + } + delta=char_italic(cur_f, cur_i);x=clean_box(nucleus(q), cur_style); + if ((math_type(subscr(q))!=empty)&&(subtype(q)!=limits)) + width(x)=width(x)-delta; /*remove italic correction*/ + shift_amount(x)=half(height(x)-depth(x))-axis_height(cur_size); + /*center vertically*/ + math_type(nucleus(q))=sub_box;info(nucleus(q))=x; + } +else delta=0; +if (subtype(q)==limits) + @<Construct a box with limits above and below it, skewed by |delta|@>; +return delta; +} + +@ The following program builds a vlist box |v| for displayed limits. The +width of the box is not affected by the fact that the limits may be skewed. + +@<Construct a box with limits above and below it...@>= +{@+x=clean_box(supscr(q), sup_style(cur_style)); +y=clean_box(nucleus(q), cur_style); +z=clean_box(subscr(q), sub_style(cur_style)); +v=new_null_box();type(v)=vlist_node;width(v)=width(y); +if (width(x) > width(v)) width(v)=width(x); +if (width(z) > width(v)) width(v)=width(z); +x=rebox(x, width(v));y=rebox(y, width(v));z=rebox(z, width(v));@/ +shift_amount(x)=half(delta);shift_amount(z)=-shift_amount(x); +height(v)=height(y);depth(v)=depth(y); +@<Attach the limits to |y| and adjust |height(v)|, |depth(v)| to account for +their presence@>; +new_hlist(q)=v; +} + +@ We use |shift_up| and |shift_down| in the following program for the +amount of glue between the displayed operator |y| and its limits |x| and +|z|. The vlist inside box |v| will consist of |x| followed by |y| followed +by |z|, with kern nodes for the spaces between and around them. + +@<Attach the limits to |y| and adjust |height(v)|, |depth(v)|...@>= +if (math_type(supscr(q))==empty) + {@+free_node(x, box_node_size);list_ptr(v)=y; + } +else{@+shift_up=big_op_spacing3-depth(x); + if (shift_up < big_op_spacing1) shift_up=big_op_spacing1; + p=new_kern(shift_up);link(p)=y;link(x)=p;@/ + p=new_kern(big_op_spacing5);link(p)=x;list_ptr(v)=p; + height(v)=height(v)+big_op_spacing5+height(x)+depth(x)+shift_up; + } +if (math_type(subscr(q))==empty) free_node(z, box_node_size); +else{@+shift_down=big_op_spacing4-height(z); + if (shift_down < big_op_spacing2) shift_down=big_op_spacing2; + p=new_kern(shift_down);link(y)=p;link(p)=z;@/ + p=new_kern(big_op_spacing5);link(z)=p; + depth(v)=depth(v)+big_op_spacing5+height(z)+depth(z)+shift_down; + } + +@ A ligature found in a math formula does not create a |ligature_node|, because +there is no question of hyphenation afterwards; the ligature will simply be +stored in an ordinary |char_node|, after residing in an |ord_noad|. + +The |math_type| is converted to |math_text_char| here if we would not want to +apply an italic correction to the current character unless it belongs +to a math font (i.e., a font with |space==0|). + +No boundary characters enter into these ligatures. + +@<Declare math...@>= +static void make_ord(pointer @!q) +{@+ +int a; /*address of lig/kern instruction*/ +pointer @!p, @!r; /*temporary registers for list manipulation*/ +restart: @t@>@;@/ +if (math_type(subscr(q))==empty) if (math_type(supscr(q))==empty) + if (math_type(nucleus(q))==math_char) + {@+p=link(q); + if (p!=null) if ((type(p) >= ord_noad)&&(type(p) <= punct_noad)) + if (math_type(nucleus(p))==math_char) + if (fam(nucleus(p))==fam(nucleus(q))) + {@+math_type(nucleus(q))=math_text_char; + fetch(nucleus(q)); + if (char_tag(cur_i)==lig_tag) + {@+a=lig_kern_start(cur_f, cur_i); + cur_c=character(nucleus(p)); + cur_i=font_info[a].qqqq; + if (skip_byte(cur_i) > stop_flag) + {@+a=lig_kern_restart(cur_f, cur_i); + cur_i=font_info[a].qqqq; + } + loop@+{@+@<If instruction |cur_i| is a kern with |cur_c|, attach the +kern after~|q|; or if it is a ligature with |cur_c|, combine noads |q| and~|p| +appropriately; then |return| if the cursor has moved past a noad, or |goto +restart|@>; + if (skip_byte(cur_i) >= stop_flag) return; + a=a+qo(skip_byte(cur_i))+1; + cur_i=font_info[a].qqqq; + } + } + } + } +} + +@ Note that a ligature between an |ord_noad| and another kind of noad +is replaced by an |ord_noad|, when the two noads collapse into one. +But we could make a parenthesis (say) change shape when it follows +certain letters. Presumably a font designer will define such +ligatures only when this convention makes sense. + +\chardef\@@='174 % vertical line to indicate character retention + +@<If instruction |cur_i| is a kern with |cur_c|,...@>= +if (next_char(cur_i)==cur_c) if (skip_byte(cur_i) <= stop_flag) + if (op_byte(cur_i) >= kern_flag) + {@+p=new_kern(char_kern(cur_f, cur_i)); + link(p)=link(q);link(q)=p;return; + } + else{@+check_interrupt; /*allow a way out of infinite ligature loop*/ + switch (op_byte(cur_i)) { + case qi(1): case qi(5): character(nucleus(q))=rem_byte(cur_i);@+break; /*\.{=:\@@}, \.{=:\@@>}*/ + case qi(2): case qi(6): character(nucleus(p))=rem_byte(cur_i);@+break; /*\.{\@@=:}, \.{\@@=:>}*/ + case qi(3): case qi(7): case qi(11): {@+r=new_noad(); /*\.{\@@=:\@@}, \.{\@@=:\@@>}, \.{\@@=:\@@>>}*/ + character(nucleus(r))=rem_byte(cur_i); + fam(nucleus(r))=fam(nucleus(q));@/ + link(q)=r;link(r)=p; + if (op_byte(cur_i) < qi(11)) math_type(nucleus(r))=math_char; + else math_type(nucleus(r))=math_text_char; /*prevent combination*/ + } @+break; + default:{@+link(q)=link(p); + character(nucleus(q))=rem_byte(cur_i); /*\.{=:}*/ + mem[subscr(q)]=mem[subscr(p)];mem[supscr(q)]=mem[supscr(p)];@/ + free_node(p, noad_size); + } + } + if (op_byte(cur_i) > qi(3)) return; + math_type(nucleus(q))=math_char;goto restart; + } + +@ When we get to the following part of the program, we have ``fallen through'' +from cases that did not lead to |check_dimensions| or |done_with_noad| or +|done_with_node|. Thus, |q|~points to a noad whose nucleus may need to be +converted to an hlist, and whose subscripts and superscripts need to be +appended if they are present. + +If |nucleus(q)| is not a |math_char|, the variable |delta| is the amount +by which a superscript should be moved right with respect to a subscript +when both are present. +@^subscripts@> +@^superscripts@> + +@<Convert \(n)|nucleus(q)| to an hlist and attach the sub/superscripts@>= +switch (math_type(nucleus(q))) { +case math_char: case math_text_char: + @<Create a character node |p| for |nucleus(q)|, possibly followed by a kern +node for the italic correction, and set |delta| to the italic correction if +a subscript is present@>@;@+break; +case empty: p=null;@+break; +case sub_box: p=info(nucleus(q));@+break; +case sub_mlist: {@+cur_mlist=info(nucleus(q));save_style=cur_style; + mlist_penalties=false;mlist_to_hlist(); /*recursive call*/ +@^recursion@> + cur_style=save_style;@<Set up the values...@>; + p=hpack(link(temp_head), natural); + } @+break; +default:confusion("mlist2"); +@:this can't happen mlist2}{\quad mlist2@> +} @/ +new_hlist(q)=p; +if ((math_type(subscr(q))==empty)&&(math_type(supscr(q))==empty)) + goto check_dimensions; +make_scripts(q, delta) + +@ @<Create a character node |p| for |nucleus(q)|...@>= +{@+fetch(nucleus(q)); +if (char_exists(cur_i)) + {@+delta=char_italic(cur_f, cur_i);p=new_character(cur_f, qo(cur_c)); + if ((math_type(nucleus(q))==math_text_char)&&(space(cur_f)!=0)) + delta=0; /*no italic correction in mid-word of text font*/ + if ((math_type(subscr(q))==empty)&&(delta!=0)) + {@+link(p)=new_kern(delta);delta=0; + } + } +else p=null; +} + +@ The purpose of |make_scripts(q, delta)| is to attach the subscript and/or +superscript of noad |q| to the list that starts at |new_hlist(q)|, +given that the subscript and superscript aren't both empty. The superscript +will appear to the right of the subscript by a given distance |delta|. + +We set |shift_down| and |shift_up| to the minimum amounts to shift the +baseline of subscripts and superscripts based on the given nucleus. + +@<Declare math...@>= +static void make_scripts(pointer @!q, scaled @!delta) +{@+pointer p, @!x, @!y, @!z; /*temporary registers for box construction*/ +scaled @!shift_up, @!shift_down, @!clr; /*dimensions in the calculation*/ +small_number @!t; /*subsidiary size code*/ +p=new_hlist(q); +if (is_char_node(p)) + {@+shift_up=0;shift_down=0; + } +else{@+z=hpack(p, natural); + if (cur_style < script_style) t=script_size;@+else t=script_script_size; + shift_up=height(z)-sup_drop(t); + shift_down=depth(z)+sub_drop(t); + free_node(z, box_node_size); + } +if (math_type(supscr(q))==empty) + @<Construct a subscript box |x| when there is no superscript@>@; +else{@+@<Construct a superscript box |x|@>; + if (math_type(subscr(q))==empty) shift_amount(x)=-shift_up; + else@<Construct a sub/superscript combination box |x|, with the superscript +offset by |delta|@>; + } +if (new_hlist(q)==null) new_hlist(q)=x; +else{@+p=new_hlist(q); + while (link(p)!=null) p=link(p); + link(p)=x; + } +} + +@ When there is a subscript without a superscript, the top of the subscript +should not exceed the baseline plus four-fifths of the x-height. + +@<Construct a subscript box |x| when there is no superscript@>= +{@+x=clean_box(subscr(q), sub_style(cur_style)); +width(x)=width(x)+script_space; +if (shift_down < sub1(cur_size)) shift_down=sub1(cur_size); +clr=height(x)-(abs(math_x_height(cur_size)*4)/5); +if (shift_down < clr) shift_down=clr; +shift_amount(x)=shift_down; +} + +@ The bottom of a superscript should never descend below the baseline plus +one-fourth of the x-height. + +@<Construct a superscript box |x|@>= +{@+x=clean_box(supscr(q), sup_style(cur_style)); +width(x)=width(x)+script_space; +if (odd(cur_style)) clr=sup3(cur_size); +else if (cur_style < text_style) clr=sup1(cur_size); +else clr=sup2(cur_size); +if (shift_up < clr) shift_up=clr; +clr=depth(x)+(abs(math_x_height(cur_size))/4); +if (shift_up < clr) shift_up=clr; +} + +@ When both subscript and superscript are present, the subscript must be +separated from the superscript by at least four times |default_rule_thickness|. +If this condition would be violated, the subscript moves down, after which +both subscript and superscript move up so that the bottom of the superscript +is at least as high as the baseline plus four-fifths of the x-height. + +@<Construct a sub/superscript combination box |x|...@>= +{@+y=clean_box(subscr(q), sub_style(cur_style)); +width(y)=width(y)+script_space; +if (shift_down < sub2(cur_size)) shift_down=sub2(cur_size); +clr=4*default_rule_thickness- + ((shift_up-depth(x))-(height(y)-shift_down)); +if (clr > 0) + {@+shift_down=shift_down+clr; + clr=(abs(math_x_height(cur_size)*4)/5)-(shift_up-depth(x)); + if (clr > 0) + {@+shift_up=shift_up+clr; + shift_down=shift_down-clr; + } + } +shift_amount(x)=delta; /*superscript is |delta| to the right of the subscript*/ +p=new_kern((shift_up-depth(x))-(height(y)-shift_down));link(x)=p;link(p)=y; +x=vpack(x, natural);shift_amount(x)=shift_down; +} + +@ We have now tied up all the loose ends of the first pass of |mlist_to_hlist|. +The second pass simply goes through and hooks everything together with the +proper glue and penalties. It also handles the |left_noad| and |right_noad| that +might be present, since |max_h| and |max_d| are now known. Variable |p| points +to a node at the current end of the final hlist. + +@<Make a second pass over the mlist,...@>= +p=temp_head;link(p)=null;q=mlist;r_type=0;cur_style=style; +@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; +while (q!=null) + {@+@<If node |q| is a style node, change the style and |goto delete_q|; +otherwise if it is not a noad, put it into the hlist, advance |q|, and |goto +done|; otherwise set |s| to the size of noad |q|, set |t| to the associated +type (|ord_noad.. inner_noad|), and set |pen| to the associated penalty@>; + @<Append inter-element spacing based on |r_type| and |t|@>; + @<Append any |new_hlist| entries for |q|, and any appropriate penalties@>; + if (type(q)==right_noad) t=open_noad; + r_type=t; + delete_q: r=q;q=link(q);free_node(r, s); + done: ;} + +@ Just before doing the big |case| switch in the second pass, the program +sets up default values so that most of the branches are short. + +@<If node |q| is a style node, change the style...@>= +t=ord_noad;s=noad_size;pen=inf_penalty; +switch (type(q)) { +case op_noad: case open_noad: case close_noad: + case punct_noad: case inner_noad: t=type(q);@+break; +case bin_noad: {@+t=bin_noad;pen=bin_op_penalty; + } @+break; +case rel_noad: {@+t=rel_noad;pen=rel_penalty; + } @+break; +case ord_noad: case vcenter_noad: case over_noad: + case under_noad: do_nothing;@+break; +case radical_noad: s=radical_noad_size;@+break; +case accent_noad: s=accent_noad_size;@+break; +case fraction_noad: s=fraction_noad_size;@+break; +case left_noad: case right_noad: t=make_left_right(q, style, max_d, max_h);@+break; +case style_node: @<Change the current style and |goto delete_q|@>@; +case whatsit_node: case penalty_node: + case rule_node: case disc_node: case adjust_node: + case ins_node: case mark_node: + case glue_node: case kern_node: @t@>@;@/ + {@+link(p)=q;p=q;q=link(q);link(p)=null;goto done; + } +default:confusion("mlist3"); +@:this can't happen mlist3}{\quad mlist3@> +} + +@ The |make_left_right| function constructs a left or right delimiter of +the required size and returns the value |open_noad| or |close_noad|. The +|right_noad| and |left_noad| will both be based on the original |style|, +so they will have consistent sizes. + +We use the fact that |right_noad-left_noad==close_noad-open_noad|. + +@<Declare math...@>= +static small_number make_left_right(pointer @!q, small_number @!style, + scaled @!max_d, scaled @!max_h) +{@+scaled delta, @!delta1, @!delta2; /*dimensions used in the calculation*/ +cur_style=style;@<Set up the values...@>; +delta2=max_d+axis_height(cur_size); +delta1=max_h+max_d-delta2; +if (delta2 > delta1) delta1=delta2; /*|delta1| is max distance from axis*/ +delta=(delta1/500)*delimiter_factor; +delta2=delta1+delta1-delimiter_shortfall; +if (delta < delta2) delta=delta2; +new_hlist(q)=var_delimiter(delimiter(q), cur_size, delta); +return type(q)-(left_noad-open_noad); /*|open_noad| or |close_noad|*/ +} + +@ @<Change the current style and |goto delete_q|@>= +{@+cur_style=subtype(q);s=style_node_size; +@<Set up the values of |cur_size| and |cur_mu|, based on |cur_style|@>; +goto delete_q; +} + +@ The inter-element spacing in math formulas depends on an $8\times8$ table that +\TeX\ preloads as a 64-digit string. The elements of this string have the +following significance: +$$\vbox{\halign{#\hfil\cr +\.0 means no space;\cr +\.1 means a conditional thin space (\.{\\nonscript\\mskip\\thinmuskip});\cr +\.2 means a thin space (\.{\\mskip\\thinmuskip});\cr +\.3 means a conditional medium space + (\.{\\nonscript\\mskip\\medmuskip});\cr +\.4 means a conditional thick space + (\.{\\nonscript\\mskip\\thickmuskip});\cr +\.* means an impossible case.\cr}}$$ +This is all pretty cryptic, but {\sl The \TeX book\/} explains what is +supposed to happen, and the string makes it happen. +@:TeXbook}{\sl The \TeX book@> + +A global variable |magic_offset| is computed so that if |a| and |b| are +in the range |ord_noad dotdot inner_noad|, then |str_pool[a*8+b+magic_offset]| +is the digit for spacing between noad types |a| and |b|. + +If \PASCAL\ had provided a good way to preload constant arrays, this part of +the program would not have been so strange. +@:PASCAL}{\PASCAL@> + +@d math_spacing @;@/ +@t\hskip-35pt@> +"0234000122*4000133**3**344*0400400*000000234000111*1111112341011" +@t$ \hskip-35pt$@> + +@ @<Glob...@>= +static const int @!magic_offset=-9*ord_noad; /*used to find inter-element spacing*/ + +@ @<Append inter-element spacing based on |r_type| and |t|@>= +if (r_type > 0) /*not the first noad*/ + {@+switch (so(math_spacing[r_type*8+t+magic_offset])) { + case '0': x=0;@+break; + case '1': if (cur_style < script_style) x=thin_mu_skip_code;@+else x=0;@+break; + case '2': x=thin_mu_skip_code;@+break; + case '3': if (cur_style < script_style) x=med_mu_skip_code;@+else x=0;@+break; + case '4': if (cur_style < script_style) x=thick_mu_skip_code;@+else x=0;@+break; + default:confusion("mlist4"); +@:this can't happen mlist4}{\quad mlist4@> + } + if (x!=0) + {@+y=math_glue(glue_par(x), cur_mu); + z=new_glue(y);glue_ref_count(y)=null;link(p)=z;p=z;@/ + subtype(z)=x+1; /*store a symbolic subtype*/ + } + } + +@ We insert a penalty node after the hlist entries of noad |q| if |pen| +is not an ``infinite'' penalty, and if the node immediately following |q| +is not a penalty node or a |rel_noad| or absent entirely. + +@<Append any |new_hlist| entries for |q|, and any appropriate penalties@>= +if (new_hlist(q)!=null) + {@+link(p)=new_hlist(q); + @/do@+{p=link(p); + }@+ while (!(link(p)==null)); + } +if (penalties) if (link(q)!=null) if (pen < inf_penalty) + {@+r_type=type(link(q)); + if (r_type!=penalty_node) if (r_type!=rel_noad) + {@+z=new_penalty(pen);link(p)=z;p=z; + } + } + +@* Alignment. +It's sort of a miracle whenever \.{\\halign} and \.{\\valign} work, because +they cut across so many of the control structures of \TeX. + +Therefore the +present page is probably not the best place for a beginner to start reading +this program; it is better to master everything else first. + +Let us focus our thoughts on an example of what the input might be, in order +to get some idea about how the alignment miracle happens. The example doesn't +do anything useful, but it is sufficiently general to indicate all of the +special cases that must be dealt with; please do not be disturbed by its +apparent complexity and meaninglessness. +$$\vbox{\halign{\.{#}\hfil\cr +{}\\tabskip 2pt plus 3pt\cr +{}\\halign to 300pt\{u1\#v1\&\cr +\hskip 50pt\\tabskip 1pt plus 1fil u2\#v2\&\cr +\hskip 50pt u3\#v3\\cr\cr +\hskip 25pt a1\&\\omit a2\&\\vrule\\cr\cr +\hskip 25pt \\noalign\{\\vskip 3pt\}\cr +\hskip 25pt b1\\span b2\\cr\cr +\hskip 25pt \\omit\&c2\\span\\omit\\cr\}\cr}}$$ +Here's what happens: + +\yskip +(0) When `\.{\\halign to 300pt\{}' is scanned, the |scan_spec| routine +places the 300pt dimension onto the |save_stack|, and an |align_group| +code is placed above it. This will make it possible to complete the alignment +when the matching `\.\}' is found. + +(1) The preamble is scanned next. Macros in the preamble are not expanded, +@^preamble@> +except as part of a tabskip specification. For example, if \.{u2} had been +a macro in the preamble above, it would have been expanded, since \TeX\ +must look for `\.{minus...}' as part of the tabskip glue. A ``preamble list'' +is constructed based on the user's preamble; in our case it contains the +following seven items: +$$\vbox{\halign{\.{#}\hfil\qquad&(#)\hfil\cr +{}\\glue 2pt plus 3pt&the tabskip preceding column 1\cr +{}\\alignrecord, width $-\infty$&preamble info for column 1\cr +{}\\glue 2pt plus 3pt&the tabskip between columns 1 and 2\cr +{}\\alignrecord, width $-\infty$&preamble info for column 2\cr +{}\\glue 1pt plus 1fil&the tabskip between columns 2 and 3\cr +{}\\alignrecord, width $-\infty$&preamble info for column 3\cr +{}\\glue 1pt plus 1fil&the tabskip following column 3\cr}}$$ +These ``alignrecord'' entries have the same size as an |unset_node|, +since they will later be converted into such nodes. However, at the +moment they have no |type| or |subtype| fields; they have |info| fields +instead, and these |info| fields are initially set to the value |end_span|, +for reasons explained below. Furthermore, the alignrecord nodes have no +|height| or |depth| fields; these are renamed |u_part| and |v_part|, +and they point to token lists for the templates of the alignment. +For example, the |u_part| field in the first alignrecord points to the +token list `\.{u1}', i.e., the template preceding the `\.\#' for column~1. + +(2) \TeX\ now looks at what follows the \.{\\cr} that ended the preamble. +It is not `\.{\\noalign}' or `\.{\\omit}', so this input is put back to +be read again, and the template `\.{u1}' is fed to the scanner. Just +before reading `\.{u1}', \TeX\ goes into restricted horizontal mode. +Just after reading `\.{u1}', \TeX\ will see `\.{a1}', and then (when the +{\.\&} is sensed) \TeX\ will see `\.{v1}'. Then \TeX\ scans an |endv| +token, indicating the end of a column. At this point an |unset_node| is +created, containing the contents of the current hlist (i.e., `\.{u1a1v1}'). +The natural width of this unset node replaces the |width| field of the +alignrecord for column~1; in general, the alignrecords will record the +maximum natural width that has occurred so far in a given column. + +(3) Since `\.{\\omit}' follows the `\.\&', the templates for column~2 +are now bypassed. Again \TeX\ goes into restricted horizontal mode and +makes an |unset_node| from the resulting hlist; but this time the +hlist contains simply `\.{a2}'. The natural width of the new unset box +is remembered in the |width| field of the alignrecord for column~2. + +(4) A third |unset_node| is created for column 3, using essentially the +mechanism that worked for column~1; this unset box contains `\.{u3\\vrule +v3}'. The vertical rule in this case has running dimensions that will later +extend to the height and depth of the whole first row, since each |unset_node| +in a row will eventually inherit the height and depth of its enclosing box. + +(5) The first row has now ended; it is made into a single unset box +comprising the following seven items: +$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 1 column: u1a1v1\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 1 column: a2\cr +{}\\glue 1pt plus 1fil\cr +{}\\unsetbox for 1 column: u3\\vrule v3\cr +{}\\glue 1pt plus 1fil\cr}}$$ +The width of this unset row is unimportant, but it has the correct height +and depth, so the correct baselineskip glue will be computed as the row +is inserted into a vertical list. + +(6) Since `\.{\\noalign}' follows the current \.{\\cr}, \TeX\ appends +additional material (in this case \.{\\vskip 3pt}) to the vertical list. +While processing this material, \TeX\ will be in internal vertical +mode, and |no_align_group| will be on |save_stack|. + +(7) The next row produces an unset box that looks like this: +$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 2 columns: u1b1v1u2b2v2\cr +{}\\glue 1pt plus 1fil\cr +{}\\unsetbox for 1 column: {\rm(empty)}\cr +{}\\glue 1pt plus 1fil\cr}}$$ +The natural width of the unset box that spans columns 1~and~2 is stored +in a ``span node,'' which we will explain later; the |info| field of the +alignrecord for column~1 now points to the new span node, and the |info| +of the span node points to |end_span|. + +(8) The final row produces the unset box +$$\vbox{\halign{\hbox to 325pt{\qquad\.{#}\hfil}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 1 column: {\rm(empty)}\cr +{}\\glue 2pt plus 3pt\cr +{}\\unsetbox for 2 columns: u2c2v2\cr +{}\\glue 1pt plus 1fil\cr}}$$ +A new span node is attached to the alignrecord for column 2. + +(9) The last step is to compute the true column widths and to change all the +unset boxes to hboxes, appending the whole works to the vertical list that +encloses the \.{\\halign}. The rules for deciding on the final widths of +each unset column box will be explained below. + +\yskip\noindent +Note that as \.{\\halign} is being processed, we fearlessly give up control +to the rest of \TeX. At critical junctures, an alignment routine is +called upon to step in and do some little action, but most of the time +these routines just lurk in the background. It's something like +post-hypnotic suggestion. + +@ We have mentioned that alignrecords contain no |height| or |depth| fields. +Their |glue_sign| and |glue_order| are pre-empted as well, since it +is necessary to store information about what to do when a template ends. +This information is called the |extra_info| field. + +@d u_part(A) mem[A+height_offset].i /*pointer to \<u_j> token list*/ +@d v_part(A) mem[A+depth_offset].i /*pointer to \<v_j> token list*/ +@d extra_info(A) info(A+list_offset) /*info to remember during template*/ + +@ Alignments can occur within alignments, so a small stack is used to access +the alignrecord information. At each level we have a |preamble| pointer, +indicating the beginning of the preamble list; a |cur_align| pointer, +indicating the current position in the preamble list; a |cur_span| pointer, +indicating the value of |cur_align| at the beginning of a sequence of +spanned columns; a |cur_loop| pointer, indicating the tabskip glue before +an alignrecord that should be copied next if the current list is extended; +and the |align_state| variable, which indicates the nesting of braces so +that \.{\\cr} and \.{\\span} and tab marks are properly intercepted. +There also are pointers |cur_head| and |cur_tail| to the head and tail +of a list of adjustments being moved out from horizontal mode to +vertical~mode. + +The current values of these seven quantities appear in global variables; +when they have to be pushed down, they are stored in 5-word nodes, and +|align_ptr| points to the topmost such node. + +@d preamble link(align_head) /*the current preamble list*/ +@d align_stack_node_size 5 /*number of |mem| words to save alignment states*/ + +@<Glob...@>= +static pointer @!cur_align; /*current position in preamble list*/ +static pointer @!cur_span; /*start of currently spanned columns in preamble list*/ +static pointer @!cur_loop; /*place to copy when extending a periodic preamble*/ +static pointer @!align_ptr; /*most recently pushed-down alignment stack node*/ +static pointer @!cur_head, @!cur_tail; /*adjustment list pointers*/ + +@ The |align_state| and |preamble| variables are initialized elsewhere. + +@<Set init...@>= +align_ptr=null;cur_align=null;cur_span=null;cur_loop=null; +cur_head=null;cur_tail=null; + +@ Alignment stack maintenance is handled by a pair of trivial routines +called |push_alignment| and |pop_alignment|. + +@p static void push_alignment(void) +{@+pointer p; /*the new alignment stack node*/ +p=get_node(align_stack_node_size); +link(p)=align_ptr;info(p)=cur_align; +llink(p)=preamble;rlink(p)=cur_span; +mem[p+2].i=cur_loop;mem[p+3].i=align_state; +info(p+4)=cur_head;link(p+4)=cur_tail; +align_ptr=p; +cur_head=get_avail(); +} +@# +static void pop_alignment(void) +{@+pointer p; /*the top alignment stack node*/ +free_avail(cur_head); +p=align_ptr; +cur_tail=link(p+4);cur_head=info(p+4); +align_state=mem[p+3].i;cur_loop=mem[p+2].i; +cur_span=rlink(p);preamble=llink(p); +cur_align=info(p);align_ptr=link(p); +free_node(p, align_stack_node_size); +} + +@ \TeX\ has eight procedures that govern alignments: |init_align| and +|fin_align| are used at the very beginning and the very end; |init_row| and +|fin_row| are used at the beginning and end of individual rows; |init_span| +is used at the beginning of a sequence of spanned columns (possibly involving +only one column); |init_col| and |fin_col| are used at the beginning and +end of individual columns; and |align_peek| is used after \.{\\cr} to see +whether the next item is \.{\\noalign}. + +We shall consider these routines in the order they are first used during +the course of a complete \.{\\halign}, namely |init_align|, |align_peek|, +|init_row|, |init_span|, |init_col|, |fin_col|, |fin_row|, |fin_align|. + +@ When \.{\\halign} or \.{\\valign} has been scanned in an appropriate +mode, \TeX\ calls |init_align|, whose task is to get everything off to a +good start. This mostly involves scanning the preamble and putting its +information into the preamble list. +@^preamble@> + +@p @t\4@>@<Declare the procedure called |get_preamble_token|@>@t@>@/ +static void align_peek(void);@/ +static void normal_paragraph(void);@/ +static void init_align(void) +{@+ +pointer save_cs_ptr; /*|warning_index| value for error messages*/ +pointer @!p; /*for short-term temporary use*/ +save_cs_ptr=cur_cs; /*\.{\\halign} or \.{\\valign}, usually*/ +push_alignment();align_state=-1000000; /*enter a new alignment level*/ +@<Check for improper alignment in displayed math@>; +push_nest(); /*enter a new semantic level*/ +@<Change current mode to |-vmode| for \.{\\halign}, |-hmode| for \.{\\valign}@>; +scan_spec(align_group, false);@/ +@<Scan the preamble and record it in the |preamble| list@>; +new_save_level(align_group); +if (every_cr!=null) begin_token_list(every_cr, every_cr_text); +align_peek(); /*look for \.{\\noalign} or \.{\\omit}*/ +} + +@ In vertical modes, |prev_depth| already has the correct value. But +if we are in |mmode| (displayed formula mode), we reach out to the +enclosing vertical mode for the |prev_depth| value that produces the +correct baseline calculations. + +@<Change current mode...@>= +if (mode==mmode) + {@+mode=-vmode;prev_depth=nest[nest_ptr-2].aux_field.sc; + } +else if (mode > 0) negate(mode) + +@ When \.{\\halign} is used as a displayed formula, there should be +no other pieces of mlists present. + +@<Check for improper alignment in displayed math@>= +if ((mode==mmode)&&((tail!=head)||(incompleat_noad!=null))) + {@+print_err("Improper ");print_esc("halign");print(" inside $$'s"); +@.Improper \\halign...@> + help3("Displays can use special alignments (like \\eqalignno)",@/ + "only if nothing but the alignment itself is between $$'s.",@/ + "So I've deleted the formulas that preceded this alignment."); + error();flush_math(); + } + +@ @<Scan the preamble and record it in the |preamble| list@>= +preamble=null;cur_align=align_head;cur_loop=null;scanner_status=aligning; +warning_index=save_cs_ptr;align_state=-1000000; + /*at this point, |cur_cmd==left_brace|*/ +loop@+{@+@<Append the current tabskip glue to the preamble list@>; + if (cur_cmd==car_ret) goto done; /*\.{\\cr} ends the preamble*/ + @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|, looking +for changes in the tabskip glue; append an alignrecord to the preamble list@>; + } +done: scanner_status=normal + +@ @<Append the current tabskip glue to the preamble list@>= +link(cur_align)=new_param_glue(tab_skip_code); +cur_align=link(cur_align) + +@ @<Scan preamble text until |cur_cmd| is |tab_mark| or |car_ret|...@>= +@<Scan the template \<u_j>, putting the resulting token list in |hold_head|@>; +link(cur_align)=new_null_box();cur_align=link(cur_align); /*a new alignrecord*/ +info(cur_align)=end_span;width(cur_align)=null_flag; +u_part(cur_align)=link(hold_head); +@<Scan the template \<v_j>, putting the resulting token list in |hold_head|@>; +v_part(cur_align)=link(hold_head) + +@ We enter `\.{\\span}' into |eqtb| with |tab_mark| as its command code, +and with |span_code| as the command modifier. This makes \TeX\ interpret it +essentially the same as an alignment delimiter like `\.\&', yet it is +recognizably different when we need to distinguish it from a normal delimiter. +It also turns out to be useful to give a special |cr_code| to `\.{\\cr}', +and an even larger |cr_cr_code| to `\.{\\crcr}'. + +The end of a template is represented by two ``frozen'' control sequences +called \.{\\endtemplate}. The first has the command code |end_template|, which +is | > outer_call|, so it will not easily disappear in the presence of errors. +The |get_x_token| routine converts the first into the second, which has |endv| +as its command code. + +@d span_code 256 /*distinct from any character*/ +@d cr_code 257 /*distinct from |span_code| and from any character*/ +@d cr_cr_code (cr_code+1) /*this distinguishes \.{\\crcr} from \.{\\cr}*/ +@d end_template_token cs_token_flag+frozen_end_template + +@<Put each of \TeX's primitives into the hash table@>= +primitive("span", tab_mark, span_code);@/ +@!@:span\_}{\.{\\span} primitive@> +primitive("cr", car_ret, cr_code); +@!@:cr\_}{\.{\\cr} primitive@> +text(frozen_cr)=text(cur_val);eqtb[frozen_cr]=eqtb[cur_val];@/ +primitive("crcr", car_ret, cr_cr_code); +@!@:cr\_cr\_}{\.{\\crcr} primitive@> +text(frozen_end_template)=text(frozen_endv)=s_no("endtemplate"); +@.endtemplate@> +eq_type(frozen_endv)=endv;equiv(frozen_endv)=null_list; +eq_level(frozen_endv)=level_one;@/ +eqtb[frozen_end_template]=eqtb[frozen_endv]; +eq_type(frozen_end_template)=end_template; + +@ @<Cases of |print_cmd_chr|...@>= +case tab_mark: if (chr_code==span_code) print_esc("span"); + else chr_cmd("alignment tab character ")@;@+break; +case car_ret: if (chr_code==cr_code) print_esc("cr"); + else print_esc("crcr");@+break; + +@ The preamble is copied directly, except that \.{\\tabskip} causes a change +to the tabskip glue, thereby possibly expanding macros that immediately +follow it. An appearance of \.{\\span} also causes such an expansion. + +Note that if the preamble contains `\.{\\global\\tabskip}', the `\.{\\global}' +token survives in the preamble and the `\.{\\tabskip}' defines new +tabskip glue (locally). + +@<Declare the procedure called |get_preamble_token|@>= +static void get_preamble_token(void) +{@+ +restart: get_token(); +while ((cur_chr==span_code)&&(cur_cmd==tab_mark)) + {@+get_token(); /*this token will be expanded once*/ + if (cur_cmd > max_command) + {@+expand();get_token(); + } + } +if (cur_cmd==endv) + fatal_error("(interwoven alignment preambles are not allowed)"); +@.interwoven alignment preambles...@> +if ((cur_cmd==assign_glue)&&(cur_chr==glue_base+tab_skip_code)) + {@+scan_optional_equals();scan_glue(glue_val); + if (global_defs > 0) geq_define(glue_base+tab_skip_code, glue_ref, cur_val); + else eq_define(glue_base+tab_skip_code, glue_ref, cur_val); + goto restart; + } +} + +@ Spaces are eliminated from the beginning of a template. + +@<Scan the template \<u_j>...@>= +p=hold_head;link(p)=null; +loop@+{@+get_preamble_token(); + if (cur_cmd==mac_param) goto done1; + if ((cur_cmd <= car_ret)&&(cur_cmd >= tab_mark)&&(align_state==-1000000)) + if ((p==hold_head)&&(cur_loop==null)&&(cur_cmd==tab_mark) + ) cur_loop=cur_align; + else{@+print_err("Missing # inserted in alignment preamble"); +@.Missing \# inserted...@> + help3("There should be exactly one # between &'s, when an",@/ + "\\halign or \\valign is being set up. In this case you had",@/ + "none, so I've put one in; maybe that will work."); + back_error();goto done1; + } + else if ((cur_cmd!=spacer)||(p!=hold_head)) + {@+link(p)=get_avail();p=link(p);info(p)=cur_tok; + } + } +done1: + +@ @<Scan the template \<v_j>...@>= +p=hold_head;link(p)=null; +loop@+{@+resume: get_preamble_token(); + if ((cur_cmd <= car_ret)&&(cur_cmd >= tab_mark)&&(align_state==-1000000)) + goto done2; + if (cur_cmd==mac_param) + {@+print_err("Only one # is allowed per tab"); +@.Only one \# is allowed...@> + help3("There should be exactly one # between &'s, when an",@/ + "\\halign or \\valign is being set up. In this case you had",@/ + "more than one, so I'm ignoring all but the first."); + error();goto resume; + } + link(p)=get_avail();p=link(p);info(p)=cur_tok; + } +done2: link(p)=get_avail();p=link(p); +info(p)=end_template_token /*put \.{\\endtemplate} at the end*/ + +@ The tricky part about alignments is getting the templates into the +scanner at the right time, and recovering control when a row or column +is finished. + +We usually begin a row after each \.{\\cr} has been sensed, unless that +\.{\\cr} is followed by \.{\\noalign} or by the right brace that terminates +the alignment. The |align_peek| routine is used to look ahead and do +the right thing; it either gets a new row started, or gets a \.{\\noalign} +started, or finishes off the alignment. + +@<Declare the procedure called |align_peek|@>= +static void align_peek(void) +{@+ +restart: align_state=1000000; +@/do@+{get_x_or_protected(); +}@+ while (!(cur_cmd!=spacer)); +if (cur_cmd==no_align) + {@+scan_left_brace();new_save_level(no_align_group); + if (mode==-vmode) normal_paragraph(); + } +else if (cur_cmd==right_brace) fin_align(); +else if ((cur_cmd==car_ret)&&(cur_chr==cr_cr_code)) + goto restart; /*ignore \.{\\crcr}*/ +else{@+init_row(); /*start a new row*/ + init_col(); /*start a new column and replace what we peeked at*/ + } +} + +@ To start a row (i.e., a `row' that rhymes with `dough' but not with `bough'), +we enter a new semantic level, copy the first tabskip glue, and change +from internal vertical mode to restricted horizontal mode or vice versa. +The |space_factor| and |prev_depth| are not used on this semantic level, +but we clear them to zero just to be tidy. + +@p @t\4@>@<Declare the procedure called |init_span|@>@t@>@/ +static void init_row(void) +{@+push_nest();mode=(-hmode-vmode)-mode; +if (mode==-hmode) space_factor=0;@+else prev_depth=0; +tail_append(new_glue(glue_ptr(preamble))); +subtype(tail)=tab_skip_code+1;@/ +cur_align=link(preamble);cur_tail=cur_head;init_span(cur_align); +} + +@ The parameter to |init_span| is a pointer to the alignrecord where the +next column or group of columns will begin. A new semantic level is +entered, so that the columns will generate a list for subsequent packaging. + +@<Declare the procedure called |init_span|@>= +static void init_span(pointer @!p) +{@+push_nest(); +if (mode==-hmode) space_factor=1000; +else{@+prev_depth=ignore_depth;normal_paragraph(); + } +cur_span=p; +} + +@ When a column begins, we assume that |cur_cmd| is either |omit| or else +the current token should be put back into the input until the \<u_j> +template has been scanned. (Note that |cur_cmd| might be |tab_mark| or +|car_ret|.) We also assume that |align_state| is approximately 1000000 at +this time. We remain in the same mode, and start the template if it is +called for. + +@p static void init_col(void) +{@+extra_info(cur_align)=cur_cmd; +if (cur_cmd==omit) align_state=0; +else{@+back_input();begin_token_list(u_part(cur_align), u_template); + } /*now |align_state==1000000|*/ +} + +@ The scanner sets |align_state| to zero when the \<u_j> template ends. When +a subsequent \.{\\cr} or \.{\\span} or tab mark occurs with |align_state==0|, +the scanner activates the following code, which fires up the \<v_j> template. +We need to remember the |cur_chr|, which is either |cr_cr_code|, |cr_code|, +|span_code|, or a character code, depending on how the column text has ended. + +This part of the program had better not be activated when the preamble +to another alignment is being scanned, or when no alignment preamble is active. + +@<Insert the \(v)\<v_j>...@>= +{@+if ((scanner_status==aligning)||(cur_align==null)) + fatal_error("(interwoven alignment preambles are not allowed)"); +@.interwoven alignment preambles...@> +cur_cmd=extra_info(cur_align);extra_info(cur_align)=cur_chr; +if (cur_cmd==omit) begin_token_list(omit_template, v_template); +else begin_token_list(v_part(cur_align), v_template); +align_state=1000000;goto restart; +} + +@ The token list |omit_template| just referred to is a constant token +list that contains the special control sequence \.{\\endtemplate} only. + +@<Initialize the special...@>= +info(omit_template)=end_template_token; /*|link(omit_template)==null|*/ + +@ When the |endv| command at the end of a \<v_j> template comes through the +scanner, things really start to happen; and it is the |fin_col| routine +that makes them happen. This routine returns |true| if a row as well as a +column has been finished. + +@p static bool fin_col(void) +{@+ +pointer p; /*the alignrecord after the current one*/ +pointer @!q, @!r; /*temporary pointers for list manipulation*/ +pointer @!s; /*a new span node*/ +pointer @!u; /*a new unset box*/ +scaled @!w; /*natural width*/ +glue_ord @!o; /*order of infinity*/ +halfword @!n; /*span counter*/ +if (cur_align==null) confusion("endv"); +q=link(cur_align);@+if (q==null) confusion("endv"); +@:this can't happen endv}{\quad endv@> +if (align_state < 500000) + fatal_error("(interwoven alignment preambles are not allowed)"); +@.interwoven alignment preambles...@> +p=link(q); +@<If the preamble list has been traversed, check that the row has ended@>; +if (extra_info(cur_align)!=span_code) + {@+unsave();new_save_level(align_group);@/ + @<Package an unset box for the current column and record its width@>; + @<Copy the tabskip glue between columns@>; + if (extra_info(cur_align) >= cr_code) + {@+return true; + } + init_span(p); + } +align_state=1000000; +@/do@+{get_x_or_protected(); +}@+ while (!(cur_cmd!=spacer)); +cur_align=p; +init_col();return false; +} + +@ @<If the preamble list has been traversed, check that the row has ended@>= +if ((p==null)&&(extra_info(cur_align) < cr_code)) + if (cur_loop!=null) @<Lengthen the preamble periodically@>@; + else{@+print_err("Extra alignment tab has been changed to "); +@.Extra alignment tab...@> + print_esc("cr"); + help3("You have given more \\span or & marks than there were",@/ + "in the preamble to the \\halign or \\valign now in progress.",@/ + "So I'll assume that you meant to type \\cr instead."); + extra_info(cur_align)=cr_code;error(); + } + +@ @<Lengthen the preamble...@>= +{@+link(q)=new_null_box();p=link(q); /*a new alignrecord*/ +info(p)=end_span;width(p)=null_flag;cur_loop=link(cur_loop); +@<Copy the templates from node |cur_loop| into node |p|@>; +cur_loop=link(cur_loop); +link(p)=new_glue(glue_ptr(cur_loop)); +subtype(link(p))=tab_skip_code+1; +} + +@ @<Copy the templates from node |cur_loop| into node |p|@>= +q=hold_head;r=u_part(cur_loop); +while (r!=null) + {@+link(q)=get_avail();q=link(q);info(q)=info(r);r=link(r); + } +link(q)=null;u_part(p)=link(hold_head); +q=hold_head;r=v_part(cur_loop); +while (r!=null) + {@+link(q)=get_avail();q=link(q);info(q)=info(r);r=link(r); + } +link(q)=null;v_part(p)=link(hold_head) + +@ @<Copy the tabskip glue...@>= +tail_append(new_glue(glue_ptr(link(cur_align)))); +subtype(tail)=tab_skip_code+1 + +@ @<Package an unset...@>= +{@+if (mode==-hmode) + {@+adjust_tail=cur_tail;u=hpack(link(head), natural);w=width(u); + cur_tail=adjust_tail;adjust_tail=null; + } +else{@+u=vpackage(link(head), natural, 0);w=height(u); + } +n=min_quarterword; /*this represents a span count of 1*/ +if (cur_span!=cur_align) @<Update width entry for spanned columns@>@; +else if (w > width(cur_align)) width(cur_align)=w; +type(u)=unset_node;span_count(u)=n;@/ +@<Determine the stretch order@>; +glue_order(u)=o;glue_stretch(u)=total_stretch[o];@/ +@<Determine the shrink order@>; +glue_sign(u)=o;glue_shrink(u)=total_shrink[o];@/ +pop_nest();link(tail)=u;tail=u; +} + +@ A span node is a 2-word record containing |width|, |info|, and |link| +fields. The |link| field is not really a link, it indicates the number of +spanned columns; the |info| field points to a span node for the same +starting column, having a greater extent of spanning, or to |end_span|, +which has the largest possible |link| field; the |width| field holds the +largest natural width corresponding to a particular set of spanned columns. + +A list of the maximum widths so far, for spanned columns starting at a +given column, begins with the |info| field of the alignrecord for that +column. + +@d span_node_size 2 /*number of |mem| words for a span node*/ + +@<Initialize the special list heads...@>= +link(end_span)=max_quarterword+1;info(end_span)=null; + +@ @<Update width entry for spanned columns@>= +{@+q=cur_span; +@/do@+{incr(n);q=link(link(q)); +}@+ while (!(q==cur_align)); +if (n > max_quarterword) confusion("256 spans"); /*this can happen, but won't*/ +@^system dependencies@> +@:this can't happen 256 spans}{\quad 256 spans@> +q=cur_span;while (link(info(q)) < n) q=info(q); +if (link(info(q)) > n) + {@+s=get_node(span_node_size);info(s)=info(q);link(s)=n; + info(q)=s;width(s)=w; + } +else if (width(info(q)) < w) width(info(q))=w; +} + +@ At the end of a row, we append an unset box to the current vlist (for +\.{\\halign}) or the current hlist (for \.{\\valign}). This unset box +contains the unset boxes for the columns, separated by the tabskip glue. +Everything will be set later. + +@p static void fin_row(void) +{@+pointer p; /*the new unset box*/ +if (mode==-hmode) + {@+p=hpack(link(head), natural); + pop_nest();append_to_vlist(p); + if (cur_head!=cur_tail) + {@+link(tail)=link(cur_head);tail=cur_tail; + } + } +else{@+p=vpack(link(head), natural);pop_nest(); + link(tail)=p;tail=p;space_factor=1000; + } +type(p)=unset_node;glue_stretch(p)=0; +if (every_cr!=null) begin_token_list(every_cr, every_cr_text); +align_peek(); +} /*note that |glue_shrink(p)==0| since |glue_shrink====shift_amount|*/ + +@ Finally, we will reach the end of the alignment, and we can breathe a +sigh of relief that memory hasn't overflowed. All the unset boxes will now be +set so that the columns line up, taking due account of spanned columns. + +@p static void do_assignments(void);@/ +static void resume_after_display(void);@/ +static void build_page(void);@/ +static void fin_align(void) +{@+pointer @!p, @!q, @!r, @!s, @!u, @!v; /*registers for the list operations*/ +scaled @!t, @!w; /*width of column*/ +scaled @!o; /*shift offset for unset boxes*/ +halfword @!n; /*matching span amount*/ +scaled @!rule_save; /*temporary storage for |overfull_rule|*/ +memory_word @!aux_save; /*temporary storage for |aux|*/ +if (cur_group!=align_group) confusion("align1"); +@:this can't happen align}{\quad align@> +unsave(); /*that |align_group| was for individual entries*/ +if (cur_group!=align_group) confusion("align0"); +unsave(); /*that |align_group| was for the whole alignment*/ +if (nest[nest_ptr-1].mode_field==mmode) o=display_indent; + else o=0; +@<Go through the preamble list, determining the column widths and changing +the alignrecords to dummy unset boxes@>; +@<Package the preamble list, to determine the actual tabskip glue amounts, +and let |p| point to this prototype box@>; +@<Set the glue in all the unset boxes of the current list@>; +flush_node_list(p);pop_alignment(); +@<Insert the \(c)current list into its environment@>; +} @/ +@t\4@>@<Declare the procedure called |align_peek|@>@; + +@ It's time now to dismantle the preamble list and to compute the column +widths. Let $w_{ij}$ be the maximum of the natural widths of all entries +that span columns $i$ through $j$, inclusive. The alignrecord for column~$i$ +contains $w_{ii}$ in its |width| field, and there is also a linked list of +the nonzero $w_{ij}$ for increasing $j$, accessible via the |info| field; +these span nodes contain the value $j-i+|min_quarterword|$ in their +|link| fields. The values of $w_{ii}$ were initialized to |null_flag|, which +we regard as $-\infty$. + +The final column widths are defined by the formula +$$w_j=\max_{1\le i\le j}\biggl( w_{ij}-\sum_{i\le k<j}(t_k+w_k)\biggr),$$ +where $t_k$ is the natural width of the tabskip glue between columns +$k$ and~$k+1$. However, if $w_{ij}=-\infty$ for all |i| in the range +|1 <= i <= j| (i.e., if every entry that involved column~|j| also involved +column~|j+1|), we let $w_j=0$, and we zero out the tabskip glue after +column~|j|. + +\TeX\ computes these values by using the following scheme: First $w_1=w_{11}$. +Then replace $w_{2j}$ by $\max(w_{2j},w_{1j}-t_1-w_1)$, for all $j>1$. +Then $w_2=w_{22}$. Then replace $w_{3j}$ by $\max(w_{3j},w_{2j}-t_2-w_2)$ +for all $j>2$; and so on. If any $w_j$ turns out to be $-\infty$, its +value is changed to zero and so is the next tabskip. + +@<Go through the preamble list,...@>= +q=link(preamble); +@/do@+{flush_list(u_part(q));flush_list(v_part(q)); +p=link(link(q)); +if (width(q)==null_flag) + @<Nullify |width(q)| and the tabskip glue following this column@>; +if (info(q)!=end_span) + @<Merge the widths in the span nodes of |q| with those of |p|, destroying +the span nodes of |q|@>; +type(q)=unset_node;span_count(q)=min_quarterword;height(q)=0; +depth(q)=0;glue_order(q)=normal;glue_sign(q)=normal; +glue_stretch(q)=0;glue_shrink(q)=0;q=p; +}@+ while (!(q==null)) + +@ @<Nullify |width(q)| and the tabskip glue following this column@>= +{@+width(q)=0;r=link(q);s=glue_ptr(r); +if (s!=zero_glue) + {@+add_glue_ref(zero_glue);delete_glue_ref(s); + glue_ptr(r)=zero_glue; + } +} + +@ Merging of two span-node lists is a typical exercise in the manipulation of +linearly linked data structures. The essential invariant in the following +|@/do@+{| loop is that we want to dispense with node |r|, in |q|'s list, +and |u| is its successor; all nodes of |p|'s list up to and including |s| +have been processed, and the successor of |s| matches |r| or precedes |r| +or follows |r|, according as |link(r)==n| or |link(r) > n| or |link(r) < n|. + +@<Merge the widths...@>= +{@+t=width(q)+width(glue_ptr(link(q))); +r=info(q);s=end_span;info(s)=p;n=min_quarterword+1; +@/do@+{width(r)=width(r)-t;u=info(r); +while (link(r) > n) + {@+s=info(s);n=link(info(s))+1; + } +if (link(r) < n) + {@+info(r)=info(s);info(s)=r;decr(link(r));s=r; + } +else{@+if (width(r) > width(info(s))) width(info(s))=width(r); + free_node(r, span_node_size); + } +r=u; +}@+ while (!(r==end_span)); +} + +@ Now the preamble list has been converted to a list of alternating unset +boxes and tabskip glue, where the box widths are equal to the final +column sizes. In case of \.{\\valign}, we change the widths to heights, +so that a correct error message will be produced if the alignment is +overfull or underfull. + +@<Package the preamble list...@>= +save_ptr=save_ptr-2;pack_begin_line=-mode_line; +if (mode==-vmode) + {@+rule_save=overfull_rule; + overfull_rule=0; /*prevent rule from being packaged*/ + p=hpack(preamble, saved(1), saved(0));overfull_rule=rule_save; + } +else{@+q=link(preamble); + @/do@+{height(q)=width(q);width(q)=0;q=link(link(q)); + }@+ while (!(q==null)); + p=vpack(preamble, saved(1), saved(0)); + q=link(preamble); + @/do@+{width(q)=height(q);height(q)=0;q=link(link(q)); + }@+ while (!(q==null)); + } +pack_begin_line=0 + +@ @<Set the glue in all the unset...@>= +q=link(head);s=head; +while (q!=null) + {@+if (!is_char_node(q)) + if (type(q)==unset_node) + @<Set the unset box |q| and the unset boxes in it@>@; + else if (type(q)==rule_node) + @<Make the running dimensions in rule |q| extend to the boundaries of +the alignment@>; + s=q;q=link(q); + } + +@ @<Make the running dimensions in rule |q| extend...@>= +{@+if (is_running(width(q))) width(q)=width(p); +if (is_running(height(q))) height(q)=height(p); +if (is_running(depth(q))) depth(q)=depth(p); +if (o!=0) + {@+r=link(q);link(q)=null;q=hpack(q, natural); + shift_amount(q)=o;link(q)=r;link(s)=q; + } +} + +@ The unset box |q| represents a row that contains one or more unset boxes, +depending on how soon \.{\\cr} occurred in that row. + +@<Set the unset box |q| and the unset boxes in it@>= +{@+if (mode==-vmode) + {@+type(q)=hlist_node;width(q)=width(p); + } +else{@+type(q)=vlist_node;height(q)=height(p); + } +glue_order(q)=glue_order(p);glue_sign(q)=glue_sign(p); +glue_set(q)=glue_set(p);shift_amount(q)=o; +r=link(list_ptr(q));s=link(list_ptr(p)); +@/do@+{@<Set the glue in node |r| and change it from an unset node@>; +r=link(link(r));s=link(link(s)); +}@+ while (!(r==null)); +} + +@ A box made from spanned columns will be followed by tabskip glue nodes and +by empty boxes as if there were no spanning. This permits perfect alignment +of subsequent entries, and it prevents values that depend on floating point +arithmetic from entering into the dimensions of any boxes. + +@<Set the glue in node |r|...@>= +n=span_count(r);t=width(s);w=t;u=hold_head; +while (n > min_quarterword) + {@+decr(n); + @<Append tabskip glue and an empty box to list |u|, and update |s| and |t| +as the prototype nodes are passed@>; + } +if (mode==-vmode) + @<Make the unset node |r| into an |hlist_node| of width |w|, setting the +glue as if the width were |t|@>@; +else@<Make the unset node |r| into a |vlist_node| of height |w|, setting the +glue as if the height were |t|@>; +shift_amount(r)=0; +if (u!=hold_head) /*append blank boxes to account for spanned nodes*/ + {@+link(u)=link(r);link(r)=link(hold_head);r=u; + } + +@ @<Append tabskip glue and an empty box to list |u|...@>= +s=link(s);v=glue_ptr(s);link(u)=new_glue(v);u=link(u); +subtype(u)=tab_skip_code+1;t=t+width(v); +if (glue_sign(p)==stretching) + {@+if (stretch_order(v)==glue_order(p)) + t=t+round(unfix(glue_set(p))*stretch(v)); +@^real multiplication@> + } +else if (glue_sign(p)==shrinking) + {@+if (shrink_order(v)==glue_order(p)) + t=t-round(unfix(glue_set(p))*shrink(v)); + } +s=link(s);link(u)=new_null_box();u=link(u);t=t+width(s); +if (mode==-vmode) width(u)=width(s);@+else + {@+type(u)=vlist_node;height(u)=width(s); + } + +@ @<Make the unset node |r| into an |hlist_node| of width |w|...@>= +{@+height(r)=height(q);depth(r)=depth(q); +if (t==width(r)) + {@+glue_sign(r)=normal;glue_order(r)=normal; + set_glue_ratio_zero(glue_set(r)); + } +else if (t > width(r)) + {@+glue_sign(r)=stretching; + if (glue_stretch(r)==0) set_glue_ratio_zero(glue_set(r)); + else glue_set(r)=fix((t-width(r))/(double)glue_stretch(r)); +@^real division@> + } +else{@+glue_order(r)=glue_sign(r);glue_sign(r)=shrinking; + if (glue_shrink(r)==0) set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r)==normal)&&(width(r)-t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else glue_set(r)=fix((width(r)-t)/(double)glue_shrink(r)); + } +width(r)=w;type(r)=hlist_node; +} + +@ @<Make the unset node |r| into a |vlist_node| of height |w|...@>= +{@+width(r)=width(q); +if (t==height(r)) + {@+glue_sign(r)=normal;glue_order(r)=normal; + set_glue_ratio_zero(glue_set(r)); + } +else if (t > height(r)) + {@+glue_sign(r)=stretching; + if (glue_stretch(r)==0) set_glue_ratio_zero(glue_set(r)); + else glue_set(r)=fix((t-height(r))/(double)glue_stretch(r)); +@^real division@> + } +else{@+glue_order(r)=glue_sign(r);glue_sign(r)=shrinking; + if (glue_shrink(r)==0) set_glue_ratio_zero(glue_set(r)); + else if ((glue_order(r)==normal)&&(height(r)-t > glue_shrink(r))) + set_glue_ratio_one(glue_set(r)); + else glue_set(r)=fix((height(r)-t)/(double)glue_shrink(r)); + } +height(r)=w;type(r)=vlist_node; +} + +@ We now have a completed alignment, in the list that starts at |head| +and ends at |tail|. This list will be merged with the one that encloses +it. (In case the enclosing mode is |mmode|, for displayed formulas, +we will need to insert glue before and after the display; that part of the +program will be deferred until we're more familiar with such operations.) + +In restricted horizontal mode, the |clang| part of |aux| is undefined; +an over-cautious \PASCAL\ runtime system may complain about this. +@^dirty \PASCAL@> + +@<Insert the \(c)current list into its environment@>= +aux_save=aux;p=link(head);q=tail;pop_nest(); +if (mode==mmode) @<Finish an alignment in a display@>@; +else{@+aux=aux_save;link(tail)=p; + if (p!=null) tail=q; + if (mode==vmode) build_page(); + } + +@* Breaking paragraphs into lines. +We come now to what is probably the most interesting algorithm of \TeX: +the mechanism for choosing the ``best possible'' breakpoints that yield +the individual lines of a paragraph. \TeX's line-breaking algorithm takes +a given horizontal list and converts it to a sequence of boxes that are +appended to the current vertical list. In the course of doing this, it +creates a special data structure containing three kinds of records that are +not used elsewhere in \TeX. Such nodes are created while a paragraph is +being processed, and they are destroyed afterwards; thus, the other parts +of \TeX\ do not need to know anything about how line-breaking is done. + +The method used here is based on an approach devised by Michael F. Plass and +@^Plass, Michael Frederick@> +@^Knuth, Donald Ervin@> +the author in 1977, subsequently generalized and improved by the same two +people in 1980. A detailed discussion appears in {\sl Software---Practice +and Experience \bf11} (1981), 1119--1184, where it is shown that the +line-breaking problem can be regarded as a special case of the problem of +computing the shortest path in an acyclic network. The cited paper includes +numerous examples and describes the history of line breaking as it has been +practiced by printers through the ages. The present implementation adds two +new ideas to the algorithm of 1980: Memory space requirements are considerably +reduced by using smaller records for inactive nodes than for active ones, +and arithmetic overflow is avoided by using ``delta distances'' instead of +keeping track of the total distance from the beginning of the paragraph to the +current point. + +@ The |line_break| procedure should be invoked only in horizontal mode; it +leaves that mode and places its output into the current vlist of the +enclosing vertical mode (or internal vertical mode). +There is one explicit parameter: |final_widow_penalty| is the amount of +additional penalty to be inserted before the final line of the paragraph. + +There are also a number of implicit parameters: The hlist to be broken +starts at |link(head)|, and it is nonempty. The value of |prev_graf| in the +enclosing semantic level tells where the paragraph should begin in the +sequence of line numbers, in case hanging indentation or \.{\\parshape} +is in use; |prev_graf| is zero unless this paragraph is being continued +after a displayed formula. Other implicit parameters, such as the +|par_shape_ptr| and various penalties to use for hyphenation, etc., appear +in |eqtb|. + +After |line_break| has acted, it will have updated the current vlist and the +value of |prev_graf|. Furthermore, the global variable |just_box| will +point to the final box created by |line_break|, so that the width of this +line can be ascertained when it is necessary to decide whether to use +|above_display_skip| or |above_display_short_skip| before a displayed formula. + +@<Glob...@>= +static pointer @!just_box; /*the |hlist_node| for the last line of the new paragraph*/ + +@ Since |line_break| is a rather lengthy procedure---sort of a small world unto +itself---we must build it up little by little, somewhat more cautiously +than we have done with the simpler procedures of \TeX. Here is the +general outline. + +@p @t\4@>@<Declare subprocedures for |line_break|@>@; +static void line_break(int @!final_widow_penalty) +{@+ +@<Local variables for line breaking@>@; +@<Local variables to save the profiling context@>@; +@<Charge the time used here on |line_break|@>@; +pack_begin_line=mode_line; /*this is for over/underfull box messages*/ +@<Get ready to start line breaking@>; +@<Find optimal breakpoints@>; +@<Break the paragraph at the chosen breakpoints, justify the resulting lines +to the correct widths, and append them to the current vertical list@>; +@<Clean up the memory by removing the break nodes@>; +pack_begin_line=0; +@<restore the previous current file, line, and command@>@; +} +@# +@t\4@>@<Declare \eTeX\ procedures for use by |main_control|@>@; + +@ The first task is to move the list from |head| to |temp_head| and go +into the enclosing semantic level. We also append the \.{\\parfillskip} +glue to the end of the paragraph, removing a space (or other glue node) if +it was there, since spaces usually precede blank lines and instances of +`\.{\$\$}'. The |par_fill_skip| is preceded by an infinite penalty, so +it will never be considered as a potential breakpoint. + +This code assumes that a |glue_node| and a |penalty_node| occupy the +same number of |mem|~words. +@^data structure assumptions@> + +@<Get ready to start...@>= +link(temp_head)=link(head); +if (is_char_node(tail)) tail_append(new_penalty(inf_penalty))@; +else if (type(tail)!=glue_node) tail_append(new_penalty(inf_penalty))@; +else{@+type(tail)=penalty_node;delete_glue_ref(glue_ptr(tail)); + flush_node_list(leader_ptr(tail));penalty(tail)=inf_penalty; + } +link(tail)=new_param_glue(par_fill_skip_code); +init_cur_lang=prev_graf%0200000; +init_l_hyf=prev_graf/020000000; +init_r_hyf=(prev_graf/0200000)%0100; +pop_nest(); + +@ When looking for optimal line breaks, \TeX\ creates a ``break node'' for +each break that is {\sl feasible}, in the sense that there is a way to end +a line at the given place without requiring any line to stretch more than +a given tolerance. A break node is characterized by three things: the position +of the break (which is a pointer to a |glue_node|, |math_node|, |penalty_node|, +or |disc_node|); the ordinal number of the line that will follow this +breakpoint; and the fitness classification of the line that has just +ended, i.e., |tight_fit|, |decent_fit|, |loose_fit|, or |very_loose_fit|. + +@d tight_fit 3 /*fitness classification for lines shrinking 0.5 to 1.0 of their + shrinkability*/ +@d loose_fit 1 /*fitness classification for lines stretching 0.5 to 1.0 of their + stretchability*/ +@d very_loose_fit 0 /*fitness classification for lines stretching more than + their stretchability*/ +@d decent_fit 2 /*fitness classification for all other lines*/ + +@ The algorithm essentially determines the best possible way to achieve +each feasible combination of position, line, and fitness. Thus, it answers +questions like, ``What is the best way to break the opening part of the +paragraph so that the fourth line is a tight line ending at such-and-such +a place?'' However, the fact that all lines are to be the same length +after a certain point makes it possible to regard all sufficiently large +line numbers as equivalent, when the looseness parameter is zero, and this +makes it possible for the algorithm to save space and time. + +An ``active node'' and a ``passive node'' are created in |mem| for each +feasible breakpoint that needs to be considered. Active nodes are three +words long and passive nodes are two words long. We need active nodes only +for breakpoints near the place in the paragraph that is currently being +examined, so they are recycled within a comparatively short time after +they are created. + +@ An active node for a given breakpoint contains six fields: + +\yskip\hang|link| points to the next node in the list of active nodes; the +last active node has |link==last_active|. + +\yskip\hang|break_node| points to the passive node associated with this +breakpoint. + +\yskip\hang|line_number| is the number of the line that follows this +breakpoint. + +\yskip\hang|fitness| is the fitness classification of the line ending at this +breakpoint. + +\yskip\hang|type| is either |hyphenated| or |unhyphenated|, depending on +whether this breakpoint is a |disc_node|. + +\yskip\hang|total_demerits| is the minimum possible sum of demerits over all +lines leading from the beginning of the paragraph to this breakpoint. + +\yskip\noindent +The value of |link(active)| points to the first active node on a linked list +of all currently active nodes. This list is in order by |line_number|, +except that nodes with |line_number > easy_line| may be in any order relative +to each other. + +@d active_node_size 3 /*number of words in active nodes*/ +@d fitness(A) subtype(A) /*|very_loose_fit dotdot tight_fit| on final line for this break*/ +@d break_node(A) rlink(A) /*pointer to the corresponding passive node*/ +@d line_number(A) llink(A) /*line that begins at this breakpoint*/ +@d total_demerits(A) mem[A+2].i /*the quantity that \TeX\ minimizes*/ +@d unhyphenated 0 /*the |type| of a normal active break node*/ +@d hyphenated 1 /*the |type| of an active node that breaks at a |disc_node|*/ +@d last_active active /*the active list ends where it begins*/ + +@ @<Initialize the special list heads...@>= +type(last_active)=hyphenated;line_number(last_active)=max_halfword; +subtype(last_active)=0; /*the |subtype| is never examined by the algorithm*/ + +@ The passive node for a given breakpoint contains only four fields: + +\yskip\hang|link| points to the passive node created just before this one, +if any, otherwise it is |null|. + +\yskip\hang|cur_break| points to the position of this breakpoint in the +horizontal list for the paragraph being broken. + +\yskip\hang|prev_break| points to the passive node that should precede this +one in an optimal path to this breakpoint. + +\yskip\hang|serial| is equal to |n| if this passive node is the |n|th +one created during the current pass. (This field is used only when +printing out detailed statistics about the line-breaking calculations.) + +\yskip\noindent +There is a global variable called |passive| that points to the most +recently created passive node. Another global variable, |printed_node|, +is used to help print out the paragraph when detailed information about +the line-breaking computation is being displayed. + +@d passive_node_size 2 /*number of words in passive nodes*/ +@d cur_break(A) rlink(A) /*in passive node, points to position of this breakpoint*/ +@d prev_break(A) llink(A) /*points to passive node that should precede this one*/ +@d serial(A) info(A) /*serial number for symbolic identification*/ + +@<Glob...@>= +static pointer @!passive; /*most recent node on passive list*/ +static pointer @!printed_node; /*most recent node that has been printed*/ +static halfword @!pass_number; /*the number of passive nodes allocated on this pass*/ + +@ The active list also contains ``delta'' nodes that help the algorithm +compute the badness of individual lines. Such nodes appear only between two +active nodes, and they have |type==delta_node|. If |p| and |r| are active nodes +and if |q| is a delta node between them, so that |link(p)==q| and |link(q)==r|, +then |q| tells the space difference between lines in the horizontal list that +start after breakpoint |p| and lines that start after breakpoint |r|. In +other words, if we know the length of the line that starts after |p| and +ends at our current position, then the corresponding length of the line that +starts after |r| is obtained by adding the amounts in node~|q|. A delta node +contains six scaled numbers, since it must record the net change in glue +stretchability with respect to all orders of infinity. The natural width +difference appears in |mem[q+1].sc|; the stretch differences in units of +pt, fil, fill, and filll appear in |mem[q+2 dotdot q+5].sc|; and the shrink difference +appears in |mem[q+6].sc|. The |subtype| field of a delta node is not used. + +@d delta_node_size 7 /*number of words in a delta node*/ +@d delta_node 2 /*|type| field in a delta node*/ + +@ As the algorithm runs, it maintains a set of six delta-like registers +for the length of the line following the first active breakpoint to the +current position in the given hlist. When it makes a pass through the +active list, it also maintains a similar set of six registers for the +length following the active breakpoint of current interest. A third set +holds the length of an empty line (namely, the sum of \.{\\leftskip} and +\.{\\rightskip}); and a fourth set is used to create new delta nodes. + +When we pass a delta node we want to do operations like +$$\hbox{\ignorespaces|for +k=1 to 6 do cur_active_width[k]=cur_active_width[k]+mem[q+k].sc|};$$ and we +want to do this without the overhead of |for| loops. The |do_all_six| +macro makes such six-tuples convenient. + +@d do_all_six(A) A(1);A(2);A(3);A(4);A(5);A(6) + +@<Glob...@>= +static scaled @!active_width0[6], *const @!active_width = @!active_width0-1; + /*distance from first active node to~|cur_p|*/ +static scaled @!cur_active_width0[6], + *const @!cur_active_width = @!cur_active_width0-1; /*distance from current active node*/ +static scaled @!background0[6], *const @!background = @!background0-1; /*length of an ``empty'' line*/ +static scaled @!break_width0[6], *const @!break_width = @!break_width0-1; /*length being computed after current break*/ + +@ Let's state the principles of the delta nodes more precisely and concisely, +so that the following programs will be less obscure. For each legal +breakpoint~|p| in the paragraph, we define two quantities $\alpha(p)$ and +$\beta(p)$ such that the length of material in a line from breakpoint~|p| +to breakpoint~|q| is $\gamma+\beta(q)-\alpha(p)$, for some fixed $\gamma$. +Intuitively, $\alpha(p)$ and $\beta(q)$ are the total length of material from +the beginning of the paragraph to a point ``after'' a break at |p| and to a +point ``before'' a break at |q|; and $\gamma$ is the width of an empty line, +namely the length contributed by \.{\\leftskip} and \.{\\rightskip}. + +Suppose, for example, that the paragraph consists entirely of alternating +boxes and glue skips; let the boxes have widths $x_1\ldots x_n$ and +let the skips have widths $y_1\ldots y_n$, so that the paragraph can be +represented by $x_1y_1\ldots x_ny_n$. Let $p_i$ be the legal breakpoint +at $y_i$; then $\alpha(p_i)=x_1+y_1+\cdots+x_i+y_i$, and $\beta(p_i)= +x_1+y_1+\cdots+x_i$. To check this, note that the length of material from +$p_2$ to $p_5$, say, is $\gamma+x_3+y_3+x_4+y_4+x_5=\gamma+\beta(p_5) +-\alpha(p_2)$. + +The quantities $\alpha$, $\beta$, $\gamma$ involve glue stretchability and +shrinkability as well as a natural width. If we were to compute $\alpha(p)$ +and $\beta(p)$ for each |p|, we would need multiple precision arithmetic, and +the multiprecise numbers would have to be kept in the active nodes. +\TeX\ avoids this problem by working entirely with relative differences +or ``deltas.'' Suppose, for example, that the active list contains +$a_1\,\delta_1\,a_2\,\delta_2\,a_3$, where the |a|'s are active breakpoints +and the $\delta$'s are delta nodes. Then $\delta_1=\alpha(a_1)-\alpha(a_2)$ +and $\delta_2=\alpha(a_2)-\alpha(a_3)$. If the line breaking algorithm is +currently positioned at some other breakpoint |p|, the |active_width| array +contains the value $\gamma+\beta(p)-\alpha(a_1)$. If we are scanning through +the list of active nodes and considering a tentative line that runs from +$a_2$ to~|p|, say, the |cur_active_width| array will contain the value +$\gamma+\beta(p)-\alpha(a_2)$. Thus, when we move from $a_2$ to $a_3$, +we want to add $\alpha(a_2)-\alpha(a_3)$ to |cur_active_width|; and this +is just $\delta_2$, which appears in the active list between $a_2$ and +$a_3$. The |background| array contains $\gamma$. The |break_width| array +will be used to calculate values of new delta nodes when the active +list is being updated. + +@ Glue nodes in a horizontal list that is being paragraphed are not supposed to +include ``infinite'' shrinkability; that is why the algorithm maintains +four registers for stretching but only one for shrinking. If the user tries to +introduce infinite shrinkability, the shrinkability will be reset to finite +and an error message will be issued. A boolean variable |no_shrink_error_yet| +prevents this error message from appearing more than once per paragraph. + +@d check_shrinkage(A) if ((shrink_order(A)!=normal)&&(shrink(A)!=0)) + {@+A=finite_shrink(A); + } + +@<Glob...@>= +static bool @!no_shrink_error_yet; /*have we complained about infinite shrinkage?*/ + +@ @<Declare subprocedures for |line_break|@>= +static pointer finite_shrink(pointer @!p) /*recovers from infinite shrinkage*/ +{@+pointer q; /*new glue specification*/ +if (no_shrink_error_yet) + {@+no_shrink_error_yet=false; +#ifdef @!STAT + if (tracing_paragraphs > 0) end_diagnostic(true); +#endif +@; + print_err("Infinite glue shrinkage found in a paragraph"); +@.Infinite glue shrinkage...@> + help5("The paragraph just ended includes some glue that has",@/ + "infinite shrinkability, e.g., `\\hskip 0pt minus 1fil'.",@/ + "Such glue doesn't belong there---it allows a paragraph",@/ + "of any length to fit on one line. But it's safe to proceed,",@/ + "since the offensive shrinkability has been made finite."); + error(); +#ifdef @!STAT + if (tracing_paragraphs > 0) begin_diagnostic(); +#endif +@; + } +q=new_spec(p);shrink_order(q)=normal; +delete_glue_ref(p);return q; +} + +@ @<Get ready to start...@>= +no_shrink_error_yet=true;@/ +check_shrinkage(left_skip);check_shrinkage(right_skip);@/ +q=left_skip;r=right_skip;background[1]=width(q)+width(r);@/ +background[2]=0;background[3]=0;background[4]=0;background[5]=0;@/ +background[2+stretch_order(q)]=stretch(q);@/ +background[2+stretch_order(r)]=@|background[2+stretch_order(r)]+stretch(r);@/ +background[6]=shrink(q)+shrink(r); + +@ A pointer variable |cur_p| runs through the given horizontal list as we look +for breakpoints. This variable is global, since it is used both by |line_break| +and by its subprocedure |try_break|. + +Another global variable called |threshold| is used to determine the feasibility +of individual lines: Breakpoints are feasible if there is a way to reach +them without creating lines whose badness exceeds |threshold|. (The +badness is compared to |threshold| before penalties are added, so that +penalty values do not affect the feasibility of breakpoints, except that +no break is allowed when the penalty is 10000 or more.) If |threshold| +is 10000 or more, all legal breaks are considered feasible, since the +|badness| function specified above never returns a value greater than~10000. + +Up to three passes might be made through the paragraph in an attempt to find at +least one set of feasible breakpoints. On the first pass, we have +|threshold==pretolerance| and |second_pass==final_pass==false|. +If this pass fails to find a +feasible solution, |threshold| is set to |tolerance|, |second_pass| is set +|true|, and an attempt is made to hyphenate as many words as possible. +If that fails too, we add |emergency_stretch| to the background +stretchability and set |final_pass==true|. + +@<Glob...@>= +static pointer @!cur_p; /*the current breakpoint under consideration*/ +static bool @!second_pass; /*is this our second attempt to break this paragraph?*/ +static bool @!final_pass; /*is this our final attempt to break this paragraph?*/ +static int @!threshold; /*maximum badness on feasible lines*/ + +@ The heart of the line-breaking procedure is `|try_break|', a subroutine +that tests if the current breakpoint |cur_p| is feasible, by running +through the active list to see what lines of text can be made from active +nodes to~|cur_p|. If feasible breaks are possible, new break nodes are +created. If |cur_p| is too far from an active node, that node is +deactivated. + +The parameter |pi| to |try_break| is the penalty associated +with a break at |cur_p|; we have |pi==eject_penalty| if the break is forced, +and |pi==inf_penalty| if the break is illegal. + +The other parameter, |break_type|, is set to |hyphenated| or |unhyphenated|, +depending on whether or not the current break is at a |disc_node|. The +end of a paragraph is also regarded as `|hyphenated|'; this case is +distinguishable by the condition |cur_p==null|. + +@d copy_to_cur_active(A) cur_active_width[A]=active_width[A] +@<Declare subprocedures for |line_break|@>= +static void try_break(int @!pi, small_number @!break_type) +{@+ +pointer r; /*runs through the active list*/ +pointer @!prev_r; /*stays a step behind |r|*/ +halfword @!old_l; /*maximum line number in current equivalence class of lines*/ +bool @!no_break_yet; /*have we found a feasible break at |cur_p|?*/ +@<Other local variables for |try_break|@>@; +@<Make sure that |pi| is in the proper range@>; +no_break_yet=true;prev_r=active;old_l=0; +do_all_six(copy_to_cur_active); +loop@+{@+resume: r=link(prev_r); + @<If node |r| is of type |delta_node|, update |cur_active_width|, set |prev_r| +and |prev_prev_r|, then |goto resume|@>; + @<If a line number class has ended, create new active nodes for the best +feasible breaks in that class; then |return| if |r=last_active|, otherwise +compute the new |line_width|@>; + @<Consider the demerits for a line from |r| to |cur_p|; deactivate node +|r| if it should no longer be active; then |goto resume| if a line from +|r| to |cur_p| is infeasible, otherwise record a new feasible break@>; + } +end: ; +#ifdef @!STAT +@<Update the value of |printed_node| for symbolic displays@>; +#endif +@; +} + +@ @<Other local variables for |try_break|@>= +pointer @!prev_prev_r; /*a step behind |prev_r|, if |type(prev_r)==delta_node|*/ +pointer @!s; /*runs through nodes ahead of |cur_p|*/ +pointer @!q; /*points to a new node being created*/ +pointer @!v; /*points to a glue specification or a node ahead of |cur_p|*/ +int @!t; /*node count, if |cur_p| is a discretionary node*/ +internal_font_number @!f; /*used in character width calculation*/ +halfword @!l; /*line number of current active node*/ +bool @!node_r_stays_active; /*should node |r| remain in the active list?*/ +scaled @!line_width; /*the current line will be justified to this width*/ +int @!fit_class; /*possible fitness class of test line*/ +halfword @!b; /*badness of test line*/ +int @!d; /*demerits of test line*/ +bool @!artificial_demerits; /*has |d| been forced to zero?*/ +#ifdef @!STAT +pointer @!save_link; /*temporarily holds value of |link(cur_p)|*/ +#endif +scaled @!shortfall; /*used in badness calculations*/ + +@ @<Make sure that |pi| is in the proper range@>= +if (abs(pi) >= inf_penalty) + if (pi > 0) goto end; /*this breakpoint is inhibited by infinite penalty*/ + else pi=eject_penalty /*this breakpoint will be forced*/ + +@ The following code uses the fact that |type(last_active)!=delta_node|. + +@d update_width(A) @| + cur_active_width[A]=cur_active_width[A]+mem[r+A].sc + +@<If node |r|...@>= +@^inner loop@> +if (type(r)==delta_node) + {@+do_all_six(update_width); + prev_prev_r=prev_r;prev_r=r;goto resume; + } + +@ As we consider various ways to end a line at |cur_p|, in a given line number +class, we keep track of the best total demerits known, in an array with +one entry for each of the fitness classifications. For example, +|minimal_demerits[tight_fit]| contains the fewest total demerits of feasible +line breaks ending at |cur_p| with a |tight_fit| line; |best_place[tight_fit]| +points to the passive node for the break before~|cur_p| that achieves such +an optimum; and |best_pl_line[tight_fit]| is the |line_number| field in the +active node corresponding to |best_place[tight_fit]|. When no feasible break +sequence is known, the |minimal_demerits| entries will be equal to +|awful_bad|, which is $2^{30}-1$. Another variable, |minimum_demerits|, +keeps track of the smallest value in the |minimal_demerits| array. + +@d awful_bad 07777777777 /*more than a billion demerits*/ + +@<Global...@>= +static int @!minimal_demerits0[tight_fit-very_loose_fit+1], + *const @!minimal_demerits = @!minimal_demerits0-very_loose_fit; /*best total + demerits known for current line class and position, given the fitness*/ +static int @!minimum_demerits; /*best total demerits known for current line class + and position*/ +static pointer @!best_place0[tight_fit-very_loose_fit+1], + *const @!best_place = @!best_place0-very_loose_fit; /*how to achieve + |minimal_demerits|*/ +static halfword @!best_pl_line0[tight_fit-very_loose_fit+1], + *const @!best_pl_line = @!best_pl_line0-very_loose_fit; /*corresponding + line number*/ + +@ @<Get ready to start...@>= +minimum_demerits=awful_bad; +minimal_demerits[tight_fit]=awful_bad; +minimal_demerits[decent_fit]=awful_bad; +minimal_demerits[loose_fit]=awful_bad; +minimal_demerits[very_loose_fit]=awful_bad; + +@ The first part of the following code is part of \TeX's inner loop, so +we don't want to waste any time. The current active node, namely node |r|, +contains the line number that will be considered next. At the end of the +list we have arranged the data structure so that |r==last_active| and +|line_number(last_active) > old_l|. +@^inner loop@> + +@<If a line number class...@>= +{@+l=line_number(r); +if (l > old_l) + {@+ /*now we are no longer in the inner loop*/ + if ((minimum_demerits < awful_bad)&&@| + ((old_l!=easy_line)||(r==last_active))) + @<Create new active nodes for the best feasible breaks just found@>; + if (r==last_active) goto end; + @<Compute the new line width@>; + } +} + +@ It is not necessary to create new active nodes having |minimal_demerits| +greater than +|minimum_demerits+abs(adj_demerits)|, since such active nodes will never +be chosen in the final paragraph breaks. This observation allows us to +omit a substantial number of feasible breakpoints from further consideration. + +@<Create new active nodes...@>= +{@+if (no_break_yet) @<Compute the values of |break_width|@>; +@<Insert a delta node to prepare for breaks at |cur_p|@>; +if (abs(adj_demerits) >= awful_bad-minimum_demerits) + minimum_demerits=awful_bad-1; +else minimum_demerits=minimum_demerits+abs(adj_demerits); +for (fit_class=very_loose_fit; fit_class<=tight_fit; fit_class++) + {@+if (minimal_demerits[fit_class] <= minimum_demerits) + @<Insert a new active node from |best_place[fit_class]| to |cur_p|@>; + minimal_demerits[fit_class]=awful_bad; + } +minimum_demerits=awful_bad; +@<Insert a delta node to prepare for the next active node@>; +} + +@ When we insert a new active node for a break at |cur_p|, suppose this +new node is to be placed just before active node |a|; then we essentially +want to insert `$\delta\,|cur_p|\,\delta^\prime$' before |a|, where +$\delta=\alpha(a)-\alpha(|cur_p|)$ and $\delta^\prime=\alpha(|cur_p|)-\alpha(a)$ +in the notation explained above. The |cur_active_width| array now holds +$\gamma+\beta(|cur_p|)-\alpha(a)$; so $\delta$ can be obtained by +subtracting |cur_active_width| from the quantity $\gamma+\beta(|cur_p|)- +\alpha(|cur_p|)$. The latter quantity can be regarded as the length of a +line ``from |cur_p| to |cur_p|''; we call it the |break_width| at |cur_p|. + +The |break_width| is usually negative, since it consists of the background +(which is normally zero) minus the width of nodes following~|cur_p| that are +eliminated after a break. If, for example, node |cur_p| is a glue node, the +width of this glue is subtracted from the background; and we also look +ahead to eliminate all subsequent glue and penalty and kern and math +nodes, subtracting their widths as well. + +Kern nodes do not disappear at a line break unless they are |explicit|. + +@d set_break_width_to_background(A) break_width[A]=background[A] + +@<Compute the values of |break...@>= +{@+no_break_yet=false;do_all_six(set_break_width_to_background); +s=cur_p; +if (break_type > unhyphenated) if (cur_p!=null) + @<Compute the discretionary |break_width| values@>; +while (s!=null) + {@+if (is_char_node(s)) goto done; + switch (type(s)) { + case glue_node: @<Subtract glue from |break_width|@>@;@+break; + case penalty_node: do_nothing;@+break; + case math_node: break_width[1]=break_width[1]-width(s);@+break; + case kern_node: if (subtype(s)!=explicit) goto done; + else break_width[1]=break_width[1]-width(s);@+break; + default:goto done; + } @/ + s=link(s); + } +done: ;} + +@ @<Subtract glue from |break...@>= +{@+v=glue_ptr(s);break_width[1]=break_width[1]-width(v); +break_width[2+stretch_order(v)]=break_width[2+stretch_order(v)]-stretch(v); +break_width[6]=break_width[6]-shrink(v); +} + +@ When |cur_p| is a discretionary break, the length of a line ``from |cur_p| to +|cur_p|'' has to be defined properly so that the other calculations work out. +Suppose that the pre-break text at |cur_p| has length $l_0$, the post-break +text has length $l_1$, and the replacement text has length |l|. Suppose +also that |q| is the node following the replacement text. Then length of a +line from |cur_p| to |q| will be computed as $\gamma+\beta(q)-\alpha(|cur_p|)$, +where $\beta(q)=\beta(|cur_p|)-l_0+l$. The actual length will be the background +plus $l_1$, so the length from |cur_p| to |cur_p| should be $\gamma+l_0+l_1-l$. +If the post-break text of the discretionary is empty, a break may also +discard~|q|; in that unusual case we subtract the length of~|q| and any +other nodes that will be discarded after the discretionary break. + +The value of $l_0$ need not be computed, since |line_break| will put +it into the global variable |disc_width| before calling |try_break|. + +@<Glob...@>= +static scaled @!disc_width; /*the length of discretionary material preceding a break*/ + +@ @<Compute the discretionary |break...@>= +{@+t=replace_count(cur_p);v=cur_p;s=post_break(cur_p); +while (t > 0) + {@+decr(t);v=link(v); + @<Subtract the width of node |v| from |break_width|@>; + } +while (s!=null) + {@+@<Add the width of node |s| to |break_width|@>; + s=link(s); + } +break_width[1]=break_width[1]+disc_width; +if (post_break(cur_p)==null) s=link(v); + /*nodes may be discardable after the break*/ +} + +@ Replacement texts and discretionary texts are supposed to contain +only character nodes, kern nodes, ligature nodes, and box or rule nodes. + +@<Subtract the width of node |v|...@>= +if (is_char_node(v)) + {@+f=font(v); + break_width[1]=break_width[1]-char_width(f, char_info(f, character(v))); + } +else switch (type(v)) { + case ligature_node: {@+f=font(lig_char(v));@/ + break_width[1]=@|break_width[1]- + char_width(f, char_info(f, character(lig_char(v)))); + } @+break; + case hlist_node: case vlist_node: case rule_node: + case kern_node: + break_width[1]=break_width[1]-width(v);@+break; + default:confusion("disc1"); +@:this can't happen disc1}{\quad disc1@> + } + +@ @<Add the width of node |s| to |b...@>= +if (is_char_node(s)) + {@+f=font(s); + break_width[1]=@|break_width[1]+char_width(f, char_info(f, character(s))); + } +else switch (type(s)) { + case ligature_node: {@+f=font(lig_char(s)); + break_width[1]=break_width[1]+ + char_width(f, char_info(f, character(lig_char(s)))); + } @+break; + case hlist_node: case vlist_node: case rule_node: + case kern_node: + break_width[1]=break_width[1]+width(s);@+break; + default:confusion("disc2"); +@:this can't happen disc2}{\quad disc2@> + } + +@ We use the fact that |type(active)!=delta_node|. + +@d convert_to_break_width(A) @| + mem[prev_r+A].sc=@|@t\hskip10pt@>mem[prev_r+A].sc + -cur_active_width[A]+break_width[A] +@d store_break_width(A) active_width[A]=break_width[A] +@d new_delta_to_break_width(A) @| + mem[q+A].sc=break_width[A]-cur_active_width[A] + +@<Insert a delta node to prepare for breaks at |cur_p|@>= +if (type(prev_r)==delta_node) /*modify an existing delta node*/ + {@+do_all_six(convert_to_break_width); + } +else if (prev_r==active) /*no delta node needed at the beginning*/ + {@+do_all_six(store_break_width); + } +else{@+q=get_node(delta_node_size);link(q)=r;type(q)=delta_node;@/ + subtype(q)=0; /*the |subtype| is not used*/ + do_all_six(new_delta_to_break_width); + link(prev_r)=q;prev_prev_r=prev_r;prev_r=q; + } + +@ When the following code is performed, we will have just inserted at +least one active node before |r|, so |type(prev_r)!=delta_node|. + +@d new_delta_from_break_width(A) @|mem[q+A].sc= + cur_active_width[A]-break_width[A] + +@<Insert a delta node to prepare for the next active node@>= +if (r!=last_active) + {@+q=get_node(delta_node_size);link(q)=r;type(q)=delta_node;@/ + subtype(q)=0; /*the |subtype| is not used*/ + do_all_six(new_delta_from_break_width); + link(prev_r)=q;prev_prev_r=prev_r;prev_r=q; + } + +@ When we create an active node, we also create the corresponding +passive node. + +@<Insert a new active node from |best_place[fit_class]| to |cur_p|@>= +{@+q=get_node(passive_node_size); +link(q)=passive;passive=q;cur_break(q)=cur_p; +#ifdef @!STAT +incr(pass_number);serial(q)=pass_number; +#endif +@;@/ +prev_break(q)=best_place[fit_class];@/ +q=get_node(active_node_size);break_node(q)=passive; +line_number(q)=best_pl_line[fit_class]+1; +fitness(q)=fit_class;type(q)=break_type; +total_demerits(q)=minimal_demerits[fit_class]; +link(q)=r;link(prev_r)=q;prev_r=q; +#ifdef @!STAT +if (tracing_paragraphs > 0) + @<Print a symbolic description of the new break node@>; +#endif +@;@/ +} + +@ @<Print a symbolic description of the new break node@>= +{@+print_nl("@@@@");print_int(serial(passive)); +@.\AT\AT@> +print(": line ");print_int(line_number(q)-1); +print_char('.');print_int(fit_class); +if (break_type==hyphenated) print_char('-'); +print(" t=");print_int(total_demerits(q)); +print(" -> @@@@"); +if (prev_break(passive)==null) print_char('0'); +else print_int(serial(prev_break(passive))); +} + +@ The length of lines depends on whether the user has specified +\.{\\parshape} or \.{\\hangindent}. If |par_shape_ptr| is not null, it +points to a $(2n+1)$-word record in |mem|, where the |info| in the first +word contains the value of |n|, and the other $2n$ words contain the left +margins and line lengths for the first |n| lines of the paragraph; the +specifications for line |n| apply to all subsequent lines. If +|par_shape_ptr==null|, the shape of the paragraph depends on the value of +|n==hang_after|; if |n >= 0|, hanging indentation takes place on lines |n+1|, +|n+2|, \dots, otherwise it takes place on lines 1, \dots, $\vert +n\vert$. When hanging indentation is active, the left margin is +|hang_indent|, if |hang_indent >= 0|, else it is 0; the line length is +$|hsize|-\vert|hang_indent|\vert$. The normal setting is +|par_shape_ptr==null|, |hang_after==1|, and |hang_indent==0|. +Note that if |hang_indent==0|, the value of |hang_after| is irrelevant. +@^length of lines@> @^hanging indentation@> + +@<Glob...@>= +static halfword @!easy_line; /*line numbers | > easy_line| are equivalent in break nodes*/ +static halfword @!last_special_line; /*line numbers | > last_special_line| all have + the same width*/ +static scaled @!first_width; /*the width of all lines | <= last_special_line|, if + no \.{\\parshape} has been specified*/ +static scaled @!second_width; /*the width of all lines | > last_special_line|*/ +static scaled @!first_indent; /*left margin to go with |first_width|*/ +static scaled @!second_indent; /*left margin to go with |second_width|*/ + +@ We compute the values of |easy_line| and the other local variables relating +to line length when the |line_break| procedure is initializing itself. + +@<Get ready to start...@>= +if (par_shape_ptr==null) + if (hang_indent==0) + {@+last_special_line=0;second_width=hsize; + second_indent=0; + } + else@<Set line length parameters in preparation for hanging indentation@>@; +else{@+last_special_line=info(par_shape_ptr)-1; + second_width=mem[par_shape_ptr+2*(last_special_line+1)].sc; + second_indent=mem[par_shape_ptr+2*last_special_line+1].sc; + } +if (looseness==0) easy_line=last_special_line; +else easy_line=max_halfword + +@ @<Set line length parameters in preparation for hanging indentation@>= +{@+last_special_line=abs(hang_after); +if (hang_after < 0) + {@+first_width=hsize-abs(hang_indent); + if (hang_indent >= 0) first_indent=hang_indent; + else first_indent=0; + second_width=hsize;second_indent=0; + } +else{@+first_width=hsize;first_indent=0; + second_width=hsize-abs(hang_indent); + if (hang_indent >= 0) second_indent=hang_indent; + else second_indent=0; + } +} + +@ When we come to the following code, we have just encountered the first +active node~|r| whose |line_number| field contains |l|. Thus we want to +compute the length of the $l\mskip1mu$th line of the current paragraph. Furthermore, +we want to set |old_l| to the last number in the class of line numbers +equivalent to~|l|. + +@<Compute the new line width@>= +if (l > easy_line) + {@+line_width=second_width;old_l=max_halfword-1; + } +else{@+old_l=l; + if (l > last_special_line) line_width=second_width; + else if (par_shape_ptr==null) line_width=first_width; + else line_width=mem[par_shape_ptr+2*l@,].sc; + } + +@ The remaining part of |try_break| deals with the calculation of +demerits for a break from |r| to |cur_p|. + +The first thing to do is calculate the badness, |b|. This value will always +be between zero and |inf_bad+1|; the latter value occurs only in the +case of lines from |r| to |cur_p| that cannot shrink enough to fit the necessary +width. In such cases, node |r| will be deactivated. +We also deactivate node~|r| when a break at~|cur_p| is forced, since future +breaks must go through a forced break. + +@<Consider the demerits for a line from |r| to |cur_p|...@>= +{@+artificial_demerits=false;@/ +@^inner loop@> +shortfall=line_width-cur_active_width[1]; /*we're this much too short*/ +if (shortfall > 0) + @<Set the value of |b| to the badness for stretching the line, and compute +the corresponding |fit_class|@>@; +else@<Set the value of |b| to the badness for shrinking the line, and compute +the corresponding |fit_class|@>; +if ((b > inf_bad)||(pi==eject_penalty)) + @<Prepare to deactivate node~|r|, and |goto deactivate| unless there is +a reason to consider lines of text from |r| to |cur_p|@>@; +else{@+prev_r=r; + if (b > threshold) goto resume; + node_r_stays_active=true; + } +@<Record a new feasible break@>; +if (node_r_stays_active) goto resume; /*|prev_r| has been set to |r|*/ +deactivate: @<Deactivate node |r|@>; +} + +@ When a line must stretch, the available stretchability can be found in the +subarray |cur_active_width[2 dotdot 5]|, in units of points, fil, fill, and filll. + +The present section is part of \TeX's inner loop, and it is most often performed +when the badness is infinite; therefore it is worth while to make a quick +test for large width excess and small stretchability, before calling the +|badness| subroutine. +@^inner loop@> + +@<Set the value of |b| to the badness for stretching...@>= +if ((cur_active_width[3]!=0)||(cur_active_width[4]!=0)||@| + (cur_active_width[5]!=0)) + {@+b=0;fit_class=decent_fit; /*infinite stretch*/ + } +else{@+if (shortfall > 7230584) if (cur_active_width[2] < 1663497) + {@+b=inf_bad;fit_class=very_loose_fit;goto done1; + } + b=badness(shortfall, cur_active_width[2]); + if (b > 12) + if (b > 99) fit_class=very_loose_fit; + else fit_class=loose_fit; + else fit_class=decent_fit; + done1: ; + } + +@ Shrinkability is never infinite in a paragraph; +we can shrink the line from |r| to |cur_p| by at most |cur_active_width[6]|. + +@<Set the value of |b| to the badness for shrinking...@>= +{@+if (-shortfall > cur_active_width[6]) b=inf_bad+1; +else b=badness(-shortfall, cur_active_width[6]); +if (b > 12) fit_class=tight_fit;@+else fit_class=decent_fit; +} + +@ During the final pass, we dare not lose all active nodes, lest we lose +touch with the line breaks already found. The code shown here makes sure +that such a catastrophe does not happen, by permitting overfull boxes as +a last resort. This particular part of \TeX\ was a source of several subtle +bugs before the correct program logic was finally discovered; readers +who seek to ``improve'' \TeX\ should therefore think thrice before daring +to make any changes here. +@^overfull boxes@> + +@<Prepare to deactivate node~|r|, and |goto deactivate| unless...@>= +{@+if (final_pass&&(minimum_demerits==awful_bad)&&@| + (link(r)==last_active)&& + (prev_r==active)) + artificial_demerits=true; /*set demerits zero, this break is forced*/ +else if (b > threshold) goto deactivate; +node_r_stays_active=false; +} + +@ When we get to this part of the code, the line from |r| to |cur_p| is +feasible, its badness is~|b|, and its fitness classification is |fit_class|. +We don't want to make an active node for this break yet, but we will +compute the total demerits and record them in the |minimal_demerits| array, +if such a break is the current champion among all ways to get to |cur_p| +in a given line-number class and fitness class. + +@<Record a new feasible break@>= +if (artificial_demerits) d=0; +else@<Compute the demerits, |d|, from |r| to |cur_p|@>; +#ifdef @!STAT +if (tracing_paragraphs > 0) + @<Print a symbolic description of this feasible break@>; +#endif +@;@/ +d=d+total_demerits(r); /*this is the minimum total demerits + from the beginning to |cur_p| via |r|*/ +if (d <= minimal_demerits[fit_class]) + {@+minimal_demerits[fit_class]=d; + best_place[fit_class]=break_node(r);best_pl_line[fit_class]=l; + if (d < minimum_demerits) minimum_demerits=d; + } + +@ @<Print a symbolic description of this feasible break@>= +{@+if (printed_node!=cur_p) + @<Print the list between |printed_node| and |cur_p|, then set |printed_node:=cur_p|@>; +print_nl("@@"); +@.\AT@> +if (cur_p==null) print_esc("par"); +else if (type(cur_p)!=glue_node) + {@+if (type(cur_p)==penalty_node) print_esc("penalty"); + else if (type(cur_p)==disc_node) print_esc("discretionary"); + else if (type(cur_p)==kern_node) print_esc("kern"); + else print_esc("math"); + } +print(" via @@@@"); +if (break_node(r)==null) print_char('0'); +else print_int(serial(break_node(r))); +print(" b="); +if (b > inf_bad) print_char('*');@+else print_int(b); +@.*\relax@> +print(" p=");print_int(pi);print(" d="); +if (artificial_demerits) print_char('*');@+else print_int(d); +} + +@ @<Print the list between |printed_node| and |cur_p|...@>= +{@+print_nl(""); +if (cur_p==null) short_display(link(printed_node)); +else{@+save_link=link(cur_p); + link(cur_p)=null;print_nl("");short_display(link(printed_node)); + link(cur_p)=save_link; + } +printed_node=cur_p; +} + +@ When the data for a discretionary break is being displayed, we will have +printed the |pre_break| and |post_break| lists; we want to skip over the +third list, so that the discretionary data will not appear twice. The +following code is performed at the very end of |try_break|. + +@<Update the value of |printed_node|...@>= +if (cur_p==printed_node) if (cur_p!=null) if (type(cur_p)==disc_node) + {@+t=replace_count(cur_p); + while (t > 0) + {@+decr(t);printed_node=link(printed_node); + } + } + +@ @<Compute the demerits, |d|, from |r| to |cur_p|@>= +{@+d=line_penalty+b; +if (abs(d) >= 10000) d=100000000;@+else d=d*d; +if (pi!=0) + if (pi > 0) d=d+pi*pi; + else if (pi > eject_penalty) d=d-pi*pi; +if ((break_type==hyphenated)&&(type(r)==hyphenated)) + if (cur_p!=null) d=d+double_hyphen_demerits; + else d=d+final_hyphen_demerits; +if (abs(fit_class-fitness(r)) > 1) d=d+adj_demerits; +} + +@ When an active node disappears, we must delete an adjacent delta node if the +active node was at the beginning or the end of the active list, or if it +was surrounded by delta nodes. We also must preserve the property that +|cur_active_width| represents the length of material from |link(prev_r)| +to~|cur_p|. + +@d combine_two_deltas(A) @|mem[prev_r+A].sc=mem[prev_r+A].sc+mem[r+A].sc +@d downdate_width(A) @|cur_active_width[A]=cur_active_width[A]- + mem[prev_r+A].sc + +@<Deactivate node |r|@>= +link(prev_r)=link(r);free_node(r, active_node_size); +if (prev_r==active) @<Update the active widths, since the first active node +has been deleted@>@; +else if (type(prev_r)==delta_node) + {@+r=link(prev_r); + if (r==last_active) + {@+do_all_six(downdate_width); + link(prev_prev_r)=last_active; + free_node(prev_r, delta_node_size);prev_r=prev_prev_r; + } + else if (type(r)==delta_node) + {@+do_all_six(update_width); + do_all_six(combine_two_deltas); + link(prev_r)=link(r);free_node(r, delta_node_size); + } + } + +@ The following code uses the fact that |type(last_active)!=delta_node|. If the +active list has just become empty, we do not need to update the +|active_width| array, since it will be initialized when an active +node is next inserted. + +@d update_active(A) active_width[A]=active_width[A]+mem[r+A].sc + +@<Update the active widths,...@>= +{@+r=link(active); +if (type(r)==delta_node) + {@+do_all_six(update_active); + do_all_six(copy_to_cur_active); + link(active)=link(r);free_node(r, delta_node_size); + } +} + +@* Breaking paragraphs into lines, continued. +So far we have gotten a little way into the |line_break| routine, having +covered its important |try_break| subroutine. Now let's consider the +rest of the process. + +The main loop of |line_break| traverses the given hlist, +starting at |link(temp_head)|, and calls |try_break| at each legal +breakpoint. A variable called |auto_breaking| is set to true except +within math formulas, since glue nodes are not legal breakpoints when +they appear in formulas. + +The current node of interest in the hlist is pointed to by |cur_p|. Another +variable, |prev_p|, is usually one step behind |cur_p|, but the real +meaning of |prev_p| is this: If |type(cur_p)==glue_node| then |cur_p| is a legal +breakpoint if and only if |auto_breaking| is true and |prev_p| does not +point to a glue node, penalty node, explicit kern node, or math node. + +The following declarations provide for a few other local variables that are +used in special calculations. + +@<Local variables for line breaking@>= +bool @!auto_breaking; /*is node |cur_p| outside a formula?*/ +pointer @!prev_p; /*helps to determine when glue nodes are breakpoints*/ +pointer @!q, @!r, @!s, @!prev_s; /*miscellaneous nodes of temporary interest*/ +internal_font_number @!f; /*used when calculating character widths*/ + +@ The `\ignorespaces|loop|\unskip' in the following code is performed at most +thrice per call of |line_break|, since it is actually a pass over the +entire paragraph. + +@<Find optimal breakpoints@>= +threshold=pretolerance; +if (threshold >= 0) + { +#ifdef @!STAT +if (tracing_paragraphs > 0) + {@+begin_diagnostic();print_nl("@@firstpass");@+} @; +#endif +@;@/ + second_pass=false;final_pass=false; + } +else{@+threshold=tolerance;second_pass=true; + final_pass=(emergency_stretch <= 0); +#ifdef @!STAT + if (tracing_paragraphs > 0) begin_diagnostic(); +#endif +@; + } +loop@+{@+if (threshold > inf_bad) threshold=inf_bad; + if (second_pass) @<Initialize for hyphenating a paragraph@>; + @<Create an active breakpoint representing the beginning of the paragraph@>; + cur_p=link(temp_head);auto_breaking=true;@/ + prev_p=cur_p; /*glue at beginning is not a legal breakpoint*/ + while ((cur_p!=null)&&(link(active)!=last_active)) + @<Call |try_break| if |cur_p| is a legal breakpoint; on the second pass, +also try to hyphenate the next word, if |cur_p| is a glue node; then advance +|cur_p| to the next node of the paragraph that could possibly be a legal breakpoint@>; + if (cur_p==null) + @<Try the final line break at the end of the paragraph, and |goto done| +if the desired breakpoints have been found@>; + @<Clean up the memory by removing the break nodes@>; + if (!second_pass) + { +#ifdef @!STAT +if (tracing_paragraphs > 0) print_nl("@@secondpass");@; +#endif + threshold=tolerance;second_pass=true;final_pass=(emergency_stretch <= 0); + } /*if at first you don't succeed, \dots*/ + else{ +#ifdef @!STAT +if (tracing_paragraphs > 0) + print_nl("@@emergencypass");@; +#endif + background[2]=background[2]+emergency_stretch;final_pass=true; + } + } +done: +#ifdef @!STAT +if (tracing_paragraphs > 0) + {@+end_diagnostic(true);normalize_selector(); + } +#endif + +@ The active node that represents the starting point does not need a +corresponding passive node. + +@d store_background(A) active_width[A]=background[A] + +@<Create an active breakpoint representing the beginning of the paragraph@>= +q=get_node(active_node_size); +type(q)=unhyphenated;fitness(q)=decent_fit; +link(q)=last_active;break_node(q)=null; +line_number(q)=prev_graf+1;total_demerits(q)=0;link(active)=q; +do_all_six(store_background);@/ +passive=null;printed_node=temp_head;pass_number=0; +font_in_short_display=null_font + +@ @<Clean...@>= +q=link(active); +while (q!=last_active) + {@+cur_p=link(q); + if (type(q)==delta_node) free_node(q, delta_node_size); + else free_node(q, active_node_size); + q=cur_p; + } +q=passive; +while (q!=null) + {@+cur_p=link(q); + free_node(q, passive_node_size); + q=cur_p; + } + +@ Here is the main switch in the |line_break| routine, where legal breaks +are determined. As we move through the hlist, we need to keep the |active_width| +array up to date, so that the badness of individual lines is readily calculated +by |try_break|. It is convenient to use the short name |act_width| for +the component of active width that represents real width as opposed to glue. + +@d act_width active_width[1] /*length from first active node to current node*/ +@d kern_break {@+if (!is_char_node(link(cur_p))&&auto_breaking) + if (type(link(cur_p))==glue_node) try_break(0, unhyphenated); + act_width=act_width+width(cur_p); + } + +@<Call |try_break| if |cur_p| is a legal breakpoint...@>= +{@+if (is_char_node(cur_p)) + @<Advance \(c)|cur_p| to the node following the present string of characters@>; +switch (type(cur_p)) { +case hlist_node: case vlist_node: case rule_node: act_width=act_width+width(cur_p);@+break; +case whatsit_node: @<Advance \(p)past a whatsit node in the \(l)|line_break| +loop@>@;@+break; +case glue_node: {@+@<If node |cur_p| is a legal breakpoint, call |try_break|; +then update the active widths by including the glue in |glue_ptr(cur_p)|@>; + if (second_pass&&auto_breaking) + @<Try to hyphenate the following word@>; + } @+break; +case kern_node: if (subtype(cur_p)==explicit) kern_break@; + else act_width=act_width+width(cur_p);@+break; +case ligature_node: {@+f=font(lig_char(cur_p)); + act_width=act_width+char_width(f, char_info(f, character(lig_char(cur_p)))); + } @+break; +case disc_node: @<Try to break after a discretionary fragment, then |goto +done5|@>@; +case math_node: {@+auto_breaking=(subtype(cur_p)==after);kern_break; + } @+break; +case penalty_node: try_break(penalty(cur_p), unhyphenated);@+break; +case mark_node: case ins_node: case adjust_node: do_nothing;@+break; +default:confusion("paragraph"); +@:this can't happen paragraph}{\quad paragraph@> +} @/ +prev_p=cur_p;cur_p=link(cur_p); +done5: ;} + +@ The code that passes over the characters of words in a paragraph is +part of \TeX's inner loop, so it has been streamlined for speed. We use +the fact that `\.{\\parfillskip}' glue appears at the end of each paragraph; +it is therefore unnecessary to check if |link(cur_p)==null| when |cur_p| is a +character node. +@^inner loop@> + +@<Advance \(c)|cur_p| to the node following the present string...@>= +{@+prev_p=cur_p; +@/do@+{f=font(cur_p); +act_width=act_width+char_width(f, char_info(f, character(cur_p))); +cur_p=link(cur_p); +}@+ while (!(!is_char_node(cur_p))); +} + +@ When node |cur_p| is a glue node, we look at |prev_p| to see whether or not +a breakpoint is legal at |cur_p|, as explained above. + +@<If node |cur_p| is a legal breakpoint, call...@>= +if (auto_breaking) + {@+if (is_char_node(prev_p)) try_break(0, unhyphenated); + else if (precedes_break(prev_p)) try_break(0, unhyphenated); + else if ((type(prev_p)==kern_node)&&(subtype(prev_p)!=explicit)) + try_break(0, unhyphenated); + } +check_shrinkage(glue_ptr(cur_p));q=glue_ptr(cur_p); +act_width=act_width+width(q);@| +active_width[2+stretch_order(q)]=@| + active_width[2+stretch_order(q)]+stretch(q);@/ +active_width[6]=active_width[6]+shrink(q) + +@ The following code knows that discretionary texts contain +only character nodes, kern nodes, box nodes, rule nodes, and ligature nodes. + +@<Try to break after a discretionary fragment...@>= +{@+s=pre_break(cur_p);disc_width=0; +if (s==null) try_break(ex_hyphen_penalty, hyphenated); +else{@+@/do@+{@<Add the width of node |s| to |disc_width|@>; + s=link(s); + }@+ while (!(s==null)); + act_width=act_width+disc_width; + try_break(hyphen_penalty, hyphenated); + act_width=act_width-disc_width; + } +r=replace_count(cur_p);s=link(cur_p); +while (r > 0) + {@+@<Add the width of node |s| to |act_width|@>; + decr(r);s=link(s); + } +prev_p=cur_p;cur_p=s;goto done5; +} + +@ @<Add the width of node |s| to |disc_width|@>= +if (is_char_node(s)) + {@+f=font(s); + disc_width=disc_width+char_width(f, char_info(f, character(s))); + } +else switch (type(s)) { + case ligature_node: {@+f=font(lig_char(s)); + disc_width=disc_width+ + char_width(f, char_info(f, character(lig_char(s)))); + } @+break; + case hlist_node: case vlist_node: case rule_node: + case kern_node: + disc_width=disc_width+width(s);@+break; + default:confusion("disc3"); +@:this can't happen disc3}{\quad disc3@> + } + +@ @<Add the width of node |s| to |act_width|@>= +if (is_char_node(s)) + {@+f=font(s); + act_width=act_width+char_width(f, char_info(f, character(s))); + } +else switch (type(s)) { + case ligature_node: {@+f=font(lig_char(s)); + act_width=act_width+ + char_width(f, char_info(f, character(lig_char(s)))); + } @+break; + case hlist_node: case vlist_node: case rule_node: + case kern_node: + act_width=act_width+width(s);@+break; + default:confusion("disc4"); +@:this can't happen disc4}{\quad disc4@> + } + +@ The forced line break at the paragraph's end will reduce the list of +breakpoints so that all active nodes represent breaks at |cur_p==null|. +On the first pass, we insist on finding an active node that has the +correct ``looseness.'' On the final pass, there will be at least one active +node, and we will match the desired looseness as well as we can. + +The global variable |best_bet| will be set to the active node for the best +way to break the paragraph, and a few other variables are used to +help determine what is best. + +@<Glob...@>= +static pointer @!best_bet; /*use this passive node and its predecessors*/ +static int @!fewest_demerits; /*the demerits associated with |best_bet|*/ +static halfword @!best_line; /*line number following the last line of the new paragraph*/ +static int @!actual_looseness; /*the difference between |line_number(best_bet)| + and the optimum |best_line|*/ +static int @!line_diff; /*the difference between the current line number and + the optimum |best_line|*/ + +@ @<Try the final line break at the end of the paragraph...@>= +{@+try_break(eject_penalty, hyphenated); +if (link(active)!=last_active) + {@+@<Find an active node with fewest demerits@>; + if (looseness==0) goto done; + @<Find the best active node for the desired looseness@>; + if ((actual_looseness==looseness)||final_pass) goto done; + } +} + +@ @<Find an active node...@>= +r=link(active);fewest_demerits=awful_bad; +@/do@+{if (type(r)!=delta_node) if (total_demerits(r) < fewest_demerits) + {@+fewest_demerits=total_demerits(r);best_bet=r; + } +r=link(r); +}@+ while (!(r==last_active)); +best_line=line_number(best_bet) + +@ The adjustment for a desired looseness is a slightly more complicated +version of the loop just considered. Note that if a paragraph is broken +into segments by displayed equations, each segment will be subject to the +looseness calculation, independently of the other segments. + +@<Find the best active node...@>= +{@+r=link(active);actual_looseness=0; +@/do@+{if (type(r)!=delta_node) + {@+line_diff=line_number(r)-best_line; + if (((line_diff < actual_looseness)&&(looseness <= line_diff))||@| + ((line_diff > actual_looseness)&&(looseness >= line_diff))) + {@+best_bet=r;actual_looseness=line_diff; + fewest_demerits=total_demerits(r); + } + else if ((line_diff==actual_looseness)&&@| + (total_demerits(r) < fewest_demerits)) + {@+best_bet=r;fewest_demerits=total_demerits(r); + } + } +r=link(r); +}@+ while (!(r==last_active)); +best_line=line_number(best_bet); +} + +@ Once the best sequence of breakpoints has been found (hurray), we call on the +procedure |post_line_break| to finish the remainder of the work. +(By introducing this subprocedure, we are able to keep |line_break| +from getting extremely long.) + +@<Break the paragraph at the chosen...@>= +post_line_break(final_widow_penalty) + +@ The total number of lines that will be set by |post_line_break| +is |best_line-prev_graf-1|. The last breakpoint is specified by +|break_node(best_bet)|, and this passive node points to the other breakpoints +via the |prev_break| links. The finishing-up phase starts by linking the +relevant passive nodes in forward order, changing |prev_break| to +|next_break|. (The |next_break| fields actually reside in the same memory +space as the |prev_break| fields did, but we give them a new name because +of their new significance.) Then the lines are justified, one by one. + +@d next_break prev_break /*new name for |prev_break| after links are reversed*/ + +@<Declare subprocedures for |line_break|@>= +static void post_line_break(int @!final_widow_penalty) +{@+ +pointer q, @!r, @!s; /*temporary registers for list manipulation*/ +bool @!disc_break; /*was the current break at a discretionary node?*/ +bool @!post_disc_break; /*and did it have a nonempty post-break part?*/ +scaled @!cur_width; /*width of line number |cur_line|*/ +scaled @!cur_indent; /*left margin of line number |cur_line|*/ +quarterword @!t; /*used for replacement counts in discretionary nodes*/ +int @!pen; /*use when calculating penalties between lines*/ +halfword @!cur_line; /*the current line number being justified*/ +@<Reverse the links of the relevant passive nodes, setting |cur_p| to the +first breakpoint@>; +cur_line=prev_graf+1; +@/do@+{@<Justify the line ending at breakpoint |cur_p|, and append it to the +current vertical list, together with associated penalties and other insertions@>; +incr(cur_line);cur_p=next_break(cur_p); +if (cur_p!=null) if (!post_disc_break) + @<Prune unwanted nodes at the beginning of the next line@>; +}@+ while (!(cur_p==null)); +if ((cur_line!=best_line)||(link(temp_head)!=null)) + confusion("line breaking"); +@:this can't happen line breaking}{\quad line breaking@> +prev_graf=best_line-1; +} + +@ The job of reversing links in a list is conveniently regarded as the job +of taking items off one stack and putting them on another. In this case we +take them off a stack pointed to by |q| and having |prev_break| fields; +we put them on a stack pointed to by |cur_p| and having |next_break| fields. +Node |r| is the passive node being moved from stack to stack. + +@<Reverse the links of the relevant passive nodes...@>= +q=break_node(best_bet);cur_p=null; +@/do@+{r=q;q=prev_break(q);next_break(r)=cur_p;cur_p=r; +}@+ while (!(q==null)) + +@ Glue and penalty and kern and math nodes are deleted at the beginning of +a line, except in the anomalous case that the node to be deleted is actually +one of the chosen breakpoints. Otherwise +the pruning done here is designed to match +the lookahead computation in |try_break|, where the |break_width| values +are computed for non-discretionary breakpoints. + +@<Prune unwanted nodes at the beginning of the next line@>= +{@+r=temp_head; +loop@+{@+q=link(r); + if (q==cur_break(cur_p)) goto done1; + /*|cur_break(cur_p)| is the next breakpoint*/ + /*now |q| cannot be |null|*/ + if (is_char_node(q)) goto done1; + if (non_discardable(q)) goto done1; + if (type(q)==kern_node) if (subtype(q)!=explicit) goto done1; + r=q; /*now |type(q)==glue_node|, |kern_node|, |math_node|, or |penalty_node|*/ + } +done1: if (r!=temp_head) + {@+link(r)=null;flush_node_list(link(temp_head)); + link(temp_head)=q; + } +} + +@ The current line to be justified appears in a horizontal list starting +at |link(temp_head)| and ending at |cur_break(cur_p)|. If |cur_break(cur_p)| is +a glue node, we reset the glue to equal the |right_skip| glue; otherwise +we append the |right_skip| glue at the right. If |cur_break(cur_p)| is a +discretionary node, we modify the list so that the discretionary break +is compulsory, and we set |disc_break| to |true|. We also append +the |left_skip| glue at the left of the line, unless it is zero. + +@<Justify the line ending at breakpoint |cur_p|, and append it...@>= +@<Modify the end of the line to reflect the nature of the break and to include +\.{\\rightskip}; also set the proper value of |disc_break|@>; +@<Put the \(l)\.{\\leftskip} glue at the left and detach this line@>; +@<Call the packaging subroutine, setting |just_box| to the justified box@>; +@<Append the new box to the current vertical list, followed by the list of +special nodes taken out of the box by the packager@>; +@<Append a penalty node, if a nonzero penalty is appropriate@>@; + +@ At the end of the following code, |q| will point to the final node on the +list about to be justified. + +@<Modify the end of the line...@>= +q=cur_break(cur_p);disc_break=false;post_disc_break=false; +if (q!=null) /*|q| cannot be a |char_node|*/ + if (type(q)==glue_node) + {@+delete_glue_ref(glue_ptr(q)); + glue_ptr(q)=right_skip; + subtype(q)=right_skip_code+1;add_glue_ref(right_skip); + goto done; + } + else{@+if (type(q)==disc_node) + @<Change discretionary to compulsory and set |disc_break:=true|@>@; + else if ((type(q)==math_node)||(type(q)==kern_node)) width(q)=0; + } +else{@+q=temp_head; + while (link(q)!=null) q=link(q); + } +@<Put the \(r)\.{\\rightskip} glue after node |q|@>; +done: + +@ @<Change discretionary to compulsory...@>= +{@+t=replace_count(q); +@<Destroy the |t| nodes following |q|, and make |r| point to the following +node@>; +if (post_break(q)!=null) @<Transplant the post-break list@>; +if (pre_break(q)!=null) @<Transplant the pre-break list@>; +link(q)=r;disc_break=true; +} + +@ @<Destroy the |t| nodes following |q|...@>= +if (t==0) r=link(q); +else{@+r=q; + while (t > 1) + {@+r=link(r);decr(t); + } + s=link(r); + r=link(s);link(s)=null; + flush_node_list(link(q));replace_count(q)=0; + } + +@ We move the post-break list from inside node |q| to the main list by +re\-attaching it just before the present node |r|, then resetting |r|. + +@<Transplant the post-break list@>= +{@+s=post_break(q); +while (link(s)!=null) s=link(s); +link(s)=r;r=post_break(q);post_break(q)=null;post_disc_break=true; +} + +@ We move the pre-break list from inside node |q| to the main list by +re\-attaching it just after the present node |q|, then resetting |q|. + +@<Transplant the pre-break list@>= +{@+s=pre_break(q);link(q)=s; +while (link(s)!=null) s=link(s); +pre_break(q)=null;q=s; +} + +@ @<Put the \(r)\.{\\rightskip} glue after node |q|@>= +r=new_param_glue(right_skip_code);link(r)=link(q);link(q)=r;q=r + +@ The following code begins with |q| at the end of the list to be +justified. It ends with |q| at the beginning of that list, and with +|link(temp_head)| pointing to the remainder of the paragraph, if any. + +@<Put the \(l)\.{\\leftskip} glue at the left...@>= +r=link(q);link(q)=null;q=link(temp_head);link(temp_head)=r; +if (left_skip!=zero_glue) + {@+r=new_param_glue(left_skip_code); + link(r)=q;q=r; + } + +@ @<Append the new box to the current vertical list...@>= +append_to_vlist(just_box); +if (adjust_head!=adjust_tail) + {@+link(tail)=link(adjust_head);tail=adjust_tail; + } +adjust_tail=null + +@ Now |q| points to the hlist that represents the current line of the +paragraph. We need to compute the appropriate line width, pack the +line into a box of this size, and shift the box by the appropriate +amount of indentation. + +@<Call the packaging subroutine...@>= +if (cur_line > last_special_line) + {@+cur_width=second_width;cur_indent=second_indent; + } +else if (par_shape_ptr==null) + {@+cur_width=first_width;cur_indent=first_indent; + } +else{@+cur_width=mem[par_shape_ptr+2*cur_line].sc; + cur_indent=mem[par_shape_ptr+2*cur_line-1].sc; + } +adjust_tail=adjust_head;just_box=hpack(q, cur_width, exactly); +shift_amount(just_box)=cur_indent + +@ Penalties between the lines of a paragraph come from club and widow lines, +from the |inter_line_penalty| parameter, and from lines that end at +discretionary breaks. Breaking between lines of a two-line paragraph gets +both club-line and widow-line penalties. The local variable |pen| will +be set to the sum of all relevant penalties for the current line, except +that the final line is never penalized. + +@<Append a penalty node, if a nonzero penalty is appropriate@>= +if (cur_line+1!=best_line) + {@+pen=inter_line_penalty; + if (cur_line==prev_graf+1) pen=pen+club_penalty; + if (cur_line+2==best_line) pen=pen+final_widow_penalty; + if (disc_break) pen=pen+broken_penalty; + if (pen!=0) + {@+r=new_penalty(pen); + link(tail)=r;tail=r; + } + } + +@* Pre-hyphenation. +When the line-breaking routine is unable to find a feasible sequence of +breakpoints, it makes a second pass over the paragraph, attempting to +hyphenate the hyphenatable words. The goal of hyphenation is to insert +discretionary material into the paragraph so that there are more +potential places to break. + +The general rules for hyphenation are somewhat complex and technical, +because we want to be able to hyphenate words that are preceded or +followed by punctuation marks, and because we want the rules to work +for languages other than English. We also must contend with the fact +that hyphens might radically alter the ligature and kerning structure +of a word. + +A sequence of characters will be considered for hyphenation only if it +belongs to a ``potentially hyphenatable part'' of the current paragraph. +This is a sequence of nodes $p_0p_1\ldots p_m$ where $p_0$ is a glue node, +$p_1\ldots p_{m-1}$ are either character or ligature or whatsit or +implicit kern nodes, and $p_m$ is a glue or penalty or insertion or adjust +or mark or whatsit or explicit kern node. (Therefore hyphenation is +disabled by boxes, math formulas, and discretionary nodes already inserted +by the user.) The ligature nodes among $p_1\ldots p_{m-1}$ are effectively +expanded into the original non-ligature characters; the kern nodes and +whatsits are ignored. Each character |c| is now classified as either a +nonletter (if |lc_code(c)==0|), a lowercase letter (if +|lc_code(c)==c|), or an uppercase letter (otherwise); an uppercase letter +is treated as if it were |lc_code(c)| for purposes of hyphenation. The +characters generated by $p_1\ldots p_{m-1}$ may begin with nonletters; let +$c_1$ be the first letter that is not in the middle of a ligature. Whatsit +nodes preceding $c_1$ are ignored; a whatsit found after $c_1$ will be the +terminating node $p_m$. All characters that do not have the same font as +$c_1$ will be treated as nonletters. The |hyphen_char| for that font +must be between 0 and 255, otherwise hyphenation will not be attempted. +\TeX\ looks ahead for as many consecutive letters $c_1\ldots c_n$ as +possible; however, |n| must be less than 64, so a character that would +otherwise be $c_{64}$ is effectively not a letter. Furthermore $c_n$ must +not be in the middle of a ligature. In this way we obtain a string of +letters $c_1\ldots c_n$ that are generated by nodes $p_a\ldots p_b$, where +|1 <= a <= b+1 <= m|. If |n >= l_hyf+r_hyf|, this string qualifies for hyphenation; +however, |uc_hyph| must be positive, if $c_1$ is uppercase. + +The hyphenation process takes place in three stages. First, the candidate +sequence $c_1\ldots c_n$ is found; then potential positions for hyphens +are determined by referring to hyphenation tables; and finally, the nodes +$p_a\ldots p_b$ are replaced by a new sequence of nodes that includes the +discretionary breaks found. + +Fortunately, we do not have to do all this calculation very often, because +of the way it has been taken out of \TeX's inner loop. For example, when +the second edition of the author's 700-page book {\sl Seminumerical +Algorithms} was typeset by \TeX, only about 1.2 hyphenations needed to be +@^Knuth, Donald Ervin@> +tried per paragraph, since the line breaking algorithm needed to use two +passes on only about 5 per cent of the paragraphs. + +@<Initialize for hyphenating...@>= +{ +#ifdef @!INIT +if (trie_not_ready) init_trie(); +#endif +@;@/ +cur_lang=init_cur_lang;l_hyf=init_l_hyf;r_hyf=init_r_hyf; +set_hyph_index; +} + +@ The letters $c_1\ldots c_n$ that are candidates for hyphenation are placed +into an array called |hc|; the number |n| is placed into |hn|; pointers to +nodes $p_{a-1}$ and~$p_b$ in the description above are placed into variables +|ha| and |hb|; and the font number is placed into |hf|. + +@<Glob...@>= +static int16_t @!hc[66]; /*word to be hyphenated*/ +static int @!hn; /*the number of positions occupied in |hc|; + not always a |small_number|*/ +static pointer @!ha, @!hb; /*nodes |ha dotdot hb| should be replaced by the hyphenated result*/ +static internal_font_number @!hf; /*font number of the letters in |hc|*/ +static int16_t @!hu[64]; /*like |hc|, before conversion to lowercase*/ +static int @!hyf_char; /*hyphen character of the relevant font*/ +static ASCII_code @!cur_lang, @!init_cur_lang; /*current hyphenation table of interest*/ +static int @!l_hyf, @!r_hyf, @!init_l_hyf, @!init_r_hyf; /*limits on fragment sizes*/ +static halfword @!hyf_bchar; /*boundary character after $c_n$*/ + +@ Hyphenation routines need a few more local variables. + +@<Local variables for line...@>= +small_number @!j; /*an index into |hc| or |hu|*/ +int @!c; /*character being considered for hyphenation*/ + +@ When the following code is activated, the |line_break| procedure is in its +second pass, and |cur_p| points to a glue node. + +@<Try to hyphenate...@>= +{@+prev_s=cur_p;s=link(prev_s); +if (s!=null) + {@+@<Skip to node |ha|, or |goto done1| if no hyphenation should be attempted@>; + if (l_hyf+r_hyf > 63) goto done1; + @<Skip to node |hb|, putting letters into |hu| and |hc|@>; + @<Check that the nodes following |hb| permit hyphenation and that at least +|l_hyf+r_hyf| letters have been found, otherwise |goto done1|@>; + hyphenate(); + } +done1: ;} + +@ @<Declare subprocedures for |line_break|@>= +@t\4@>@<Declare the function called |reconstitute|@>@; +static void hyphenate(void) +{@+ +@<Local variables for hyphenation@>@; +@<Find hyphen locations for the word in |hc|, or |return|@>; +@<If no hyphens were found, |return|@>; +@<Replace nodes |ha..hb| by a sequence of nodes that includes the discretionary +hyphens@>; +} + +@ The first thing we need to do is find the node |ha| just before the +first letter. + +@<Skip to node |ha|, or |goto done1|...@>= +loop@+{@+if (is_char_node(s)) + {@+c=qo(character(s));hf=font(s); + } + else if (type(s)==ligature_node) + if (lig_ptr(s)==null) goto resume; + else{@+q=lig_ptr(s);c=qo(character(q));hf=font(q); + } + else if ((type(s)==kern_node)&&(subtype(s)==normal)) goto resume; + else if (type(s)==whatsit_node) + {@+@<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>; + goto resume; + } + else goto done1; + set_lc_code(c); + if (hc[0]!=0) + if ((hc[0]==c)||(uc_hyph > 0)) goto done2; + else goto done1; +resume: prev_s=s;s=link(prev_s); + } +done2: hyf_char=hyphen_char[hf]; +if (hyf_char < 0) goto done1; +if (hyf_char > 255) goto done1; +ha=prev_s + +@ The word to be hyphenated is now moved to the |hu| and |hc| arrays. + +@<Skip to node |hb|, putting letters...@>= +hn=0; +loop@+{@+if (is_char_node(s)) + {@+if (font(s)!=hf) goto done3; + hyf_bchar=character(s);c=qo(hyf_bchar); + set_lc_code(c); + if (hc[0]==0) goto done3; + if (hn==63) goto done3; + hb=s;incr(hn);hu[hn]=c;hc[hn]=hc[0];hyf_bchar=non_char; + } + else if (type(s)==ligature_node) + @<Move the characters of a ligature node to |hu| and |hc|; but |goto done3| +if they are not all letters@>@; + else if ((type(s)==kern_node)&&(subtype(s)==normal)) + {@+hb=s; + hyf_bchar=font_bchar[hf]; + } + else goto done3; + s=link(s); + } +done3: + +@ We let |j| be the index of the character being stored when a ligature node +is being expanded, since we do not want to advance |hn| until we are sure +that the entire ligature consists of letters. Note that it is possible +to get to |done3| with |hn==0| and |hb| not set to any value. + +@<Move the characters of a ligature node to |hu| and |hc|...@>= +{@+if (font(lig_char(s))!=hf) goto done3; +j=hn;q=lig_ptr(s);@+if (q > null) hyf_bchar=character(q); +while (q > null) + {@+c=qo(character(q)); + set_lc_code(c); + if (hc[0]==0) goto done3; + if (j==63) goto done3; + incr(j);hu[j]=c;hc[j]=hc[0];@/ + q=link(q); + } +hb=s;hn=j; +if (odd(subtype(s))) hyf_bchar=font_bchar[hf];@+else hyf_bchar=non_char; +} + +@ @<Check that the nodes following |hb| permit hyphenation...@>= +if (hn < l_hyf+r_hyf) goto done1; /*|l_hyf| and |r_hyf| are | >= 1|*/ +loop@+{@+if (!(is_char_node(s))) + switch (type(s)) { + case ligature_node: do_nothing;@+break; + case kern_node: if (subtype(s)!=normal) goto done4;@+break; + case whatsit_node: case glue_node: + case penalty_node: case ins_node: case adjust_node: + case mark_node: + goto done4; + default:goto done1; + } + s=link(s); + } +done4: + +@* Post-hyphenation. +If a hyphen may be inserted between |hc[j]| and |hc[j+1]|, the hyphenation +procedure will set |hyf[j]| to some small odd number. But before we look +at \TeX's hyphenation procedure, which is independent of the rest of the +line-breaking algorithm, let us consider what we will do with the hyphens +it finds, since it is better to work on this part of the program before +forgetting what |ha| and |hb|, etc., are all about. + +@<Glob...@>= +static int8_t @!hyf[65]; /*odd values indicate discretionary hyphens*/ +static pointer @!init_list; /*list of punctuation characters preceding the word*/ +static bool @!init_lig; /*does |init_list| represent a ligature?*/ +static bool @!init_lft; /*if so, did the ligature involve a left boundary?*/ + +@ @<Local variables for hyphenation@>= +int @!i, @!j, @!l; /*indices into |hc| or |hu|*/ +pointer @!q, @!r, @!s; /*temporary registers for list manipulation*/ +halfword @!bchar; /*boundary character of hyphenated word, or |non_char|*/ + +@ \TeX\ will never insert a hyphen that has fewer than +\.{\\lefthyphenmin} letters before it or fewer than +\.{\\righthyphenmin} after it; hence, a short word has +comparatively little chance of being hyphenated. If no hyphens have +been found, we can save time by not having to make any changes to the +paragraph. + +@<If no hyphens were found, |return|@>= +for (j=l_hyf; j<=hn-r_hyf; j++) if (odd(hyf[j])) goto found1; +return; +found1: + +@ If hyphens are in fact going to be inserted, \TeX\ first deletes the +subsequence of nodes between |ha| and~|hb|. An attempt is made to +preserve the effect that implicit boundary characters and punctuation marks +had on ligatures inside the hyphenated word, by storing a left boundary or +preceding character in |hu[0]| and by storing a possible right boundary +in |bchar|. We set |j=0| if |hu[0]| is to be part of the reconstruction; +otherwise |j=1|. +The variable |s| will point to the tail of the current hlist, and +|q| will point to the node following |hb|, so that +things can be hooked up after we reconstitute the hyphenated word. + +@<Replace nodes |ha..hb| by a sequence of nodes...@>= +q=link(hb);link(hb)=null;r=link(ha);link(ha)=null;bchar=hyf_bchar; +if (is_char_node(ha)) + if (font(ha)!=hf) goto found2; + else{@+init_list=ha;init_lig=false;hu[0]=qo(character(ha)); + } +else if (type(ha)==ligature_node) + if (font(lig_char(ha))!=hf) goto found2; + else{@+init_list=lig_ptr(ha);init_lig=true;init_lft=(subtype(ha) > 1); + hu[0]=qo(character(lig_char(ha))); + if (init_list==null) if (init_lft) + {@+hu[0]=256;init_lig=false; + } /*in this case a ligature will be reconstructed from scratch*/ + free_node(ha, small_node_size); + } +else{@+ /*no punctuation found; look for left boundary*/ + if (!is_char_node(r)) if (type(r)==ligature_node) + if (subtype(r) > 1) goto found2; + j=1;s=ha;init_list=null;goto common_ending; + } +s=cur_p; /*we have |cur_p!=ha| because |type(cur_p)==glue_node|*/ +while (link(s)!=ha) s=link(s); +j=0;goto common_ending; +found2: s=ha;j=0;hu[0]=256;init_lig=false;init_list=null; +common_ending: flush_node_list(r); +@<Reconstitute nodes for the hyphenated word, inserting discretionary hyphens@>; +flush_list(init_list) + +@ We must now face the fact that the battle is not over, even though the +{\def\!{\kern-1pt}% +hyphens have been found: The process of reconstituting a word can be nontrivial +because ligatures might change when a hyphen is present. {\sl The \TeX book\/} +discusses the difficulties of the word ``difficult'', and +the discretionary material surrounding a +hyphen can be considerably more complex than that. Suppose +\.{abcdef} is a word in a font for which the only ligatures are \.{b\!c}, +\.{c\!d}, \.{d\!e}, and \.{e\!f}. If this word permits hyphenation +between \.b and \.c, the two patterns with and without hyphenation are +$\.a\,\.b\,\.-\,\.{c\!d}\,\.{e\!f}$ and $\.a\,\.{b\!c}\,\.{d\!e}\,\.f$. +Thus the insertion of a hyphen might cause effects to ripple arbitrarily +far into the rest of the word. A further complication arises if additional +hyphens appear together with such rippling, e.g., if the word in the +example just given could also be hyphenated between \.c and \.d; \TeX\ +avoids this by simply ignoring the additional hyphens in such weird cases.} + +Still further complications arise in the presence of ligatures that do not +delete the original characters. When punctuation precedes the word being +hyphenated, \TeX's method is not perfect under all possible scenarios, +because punctuation marks and letters can propagate information back and forth. +For example, suppose the original pre-hyphenation pair +\.{*a} changes to \.{*y} via a \.{\@@=:} ligature, which changes to \.{xy} +via a \.{=:\@@} ligature; if $p_{a-1}=\.x$ and $p_a=\.y$, the reconstitution +procedure isn't smart enough to obtain \.{xy} again. In such cases the +font designer should include a ligature that goes from \.{xa} to \.{xy}. + +@ The processing is facilitated by a subroutine called |reconstitute|. Given +a string of characters $x_j\ldots x_n$, there is a smallest index $m\ge j$ +such that the ``translation'' of $x_j\ldots x_n$ by ligatures and kerning +has the form $y_1\ldots y_t$ followed by the translation of $x_{m+1}\ldots x_n$, +where $y_1\ldots y_t$ is some nonempty sequence of character, ligature, and +kern nodes. We call $x_j\ldots x_m$ a ``cut prefix'' of $x_j\ldots x_n$. +For example, if $x_1x_2x_3=\.{fly}$, and if the font contains `fl' as a +ligature and a kern between `fl' and `y', then $m=2$, $t=2$, and $y_1$ will +be a ligature node for `fl' followed by an appropriate kern node~$y_2$. +In the most common case, $x_j$~forms no ligature with $x_{j+1}$ and we +simply have $m=j$, $y_1=x_j$. If $m<n$ we can repeat the procedure on +$x_{m+1}\ldots x_n$ until the entire translation has been found. + +The |reconstitute| function returns the integer $m$ and puts the nodes +$y_1\ldots y_t$ into a linked list starting at |link(hold_head)|, +getting the input $x_j\ldots x_n$ from the |hu| array. If $x_j=256$, +we consider $x_j$ to be an implicit left boundary character; in this +case |j| must be strictly less than~|n|. There is a +parameter |bchar|, which is either 256 or an implicit right boundary character +assumed to be present just following~$x_n$. (The value |hu[n+1]| is never +explicitly examined, but the algorithm imagines that |bchar| is there.) + +If there exists an index |k| in the range $j\le k\le m$ such that |hyf[k]| +is odd and such that the result of |reconstitute| would have been different +if $x_{k+1}$ had been |hchar|, then |reconstitute| sets |hyphen_passed| +to the smallest such~|k|. Otherwise it sets |hyphen_passed| to zero. + +A special convention is used in the case |j==0|: Then we assume that the +translation of |hu[0]| appears in a special list of charnodes starting at +|init_list|; moreover, if |init_lig| is |true|, then |hu[0]| will be +a ligature character, involving a left boundary if |init_lft| is |true|. +This facility is provided for cases when a hyphenated +word is preceded by punctuation (like single or double quotes) that might +affect the translation of the beginning of the word. + +@<Glob...@>= +static small_number @!hyphen_passed; /*first hyphen in a ligature, if any*/ + +@ @<Declare the function called |reconstitute|@>= +static + small_number reconstitute(small_number @!j, small_number @!n, halfword @!bchar, halfword @!hchar) +{@+ +pointer @!p; /*temporary register for list manipulation*/ +pointer @!t; /*a node being appended to*/ +four_quarters @!q; /*character information or a lig/kern instruction*/ +halfword @!cur_rh; /*hyphen character for ligature testing*/ +halfword @!test_char; /*hyphen or other character for ligature testing*/ +scaled @!w; /*amount of kerning*/ +font_index @!k; /*position of current lig/kern instruction*/ +hyphen_passed=0;t=hold_head;w=0;link(hold_head)=null; + /*at this point |ligature_present==lft_hit==rt_hit==false|*/ +@<Set up data structures with the cursor following position |j|@>; +resume: @<If there's a ligature or kern at the cursor position, update the +data structures, possibly advancing~|j|; continue until the cursor moves@>; +@<Append a ligature and/or kern to the translation; |goto resume| if the +stack of inserted ligatures is nonempty@>; +return j; +} + +@ The reconstitution procedure shares many of the global data structures +by which \TeX\ has processed the words before they were hyphenated. +There is an implied ``cursor'' between characters |cur_l| and |cur_r|; +these characters will be tested for possible ligature activity. If +|ligature_present| then |cur_l| is a ligature character formed from the +original characters following |cur_q| in the current translation list. +There is a ``ligature stack'' between the cursor and character |j+1|, +consisting of pseudo-ligature nodes linked together by their |link| fields. +This stack is normally empty unless a ligature command has created a new +character that will need to be processed later. A pseudo-ligature is +a special node having a |character| field that represents a potential +ligature and a |lig_ptr| field that points to a |char_node| or is |null|. +We have +$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack > null|;\cr + \hbox{|qi(hu[j+1])|},&if |lig_stack==null| and |j < n|;\cr + bchar,&if |lig_stack==null| and |j==n|.\cr}$$ + +@<Glob...@>= +static halfword @!cur_l, @!cur_r; /*characters before and after the cursor*/ +static pointer @!cur_q; /*where a ligature should be detached*/ +static pointer @!lig_stack; /*unfinished business to the right of the cursor*/ +static bool @!ligature_present; /*should a ligature node be made for |cur_l|?*/ +static bool @!lft_hit, @!rt_hit; /*did we hit a ligature with a boundary character?*/ + +@ @d append_charnode_to_t(A) {@+link(t)=get_avail();t=link(t); + font(t)=hf;character(t)=A; + } +@d set_cur_r {@+if (j < n) cur_r=qi(hu[j+1]);@+else cur_r=bchar; + if (odd(hyf[j])) cur_rh=hchar;@+else cur_rh=non_char; + } + +@<Set up data structures with the cursor following position |j|@>= +cur_l=qi(hu[j]);cur_q=t; +if (j==0) + {@+ligature_present=init_lig;p=init_list; + if (ligature_present) lft_hit=init_lft; + while (p > null) + {@+append_charnode_to_t(character(p));p=link(p); + } + } +else if (cur_l < non_char) append_charnode_to_t(cur_l); +lig_stack=null;set_cur_r + +@ We may want to look at the lig/kern program twice, once for a hyphen +and once for a normal letter. (The hyphen might appear after the letter +in the program, so we'd better not try to look for both at once.) + +@<If there's a ligature or kern at the cursor position, update...@>= +if (cur_l==non_char) + {@+k=bchar_label[hf]; + if (k==non_address) goto done;@+else q=font_info[k].qqqq; + } +else{@+q=char_info(hf, cur_l); + if (char_tag(q)!=lig_tag) goto done; + k=lig_kern_start(hf, q);q=font_info[k].qqqq; + if (skip_byte(q) > stop_flag) + {@+k=lig_kern_restart(hf, q);q=font_info[k].qqqq; + } + } /*now |k| is the starting address of the lig/kern program*/ +if (cur_rh < non_char) test_char=cur_rh;@+else test_char=cur_r; +loop@+{@+if (next_char(q)==test_char) if (skip_byte(q) <= stop_flag) + if (cur_rh < non_char) + {@+hyphen_passed=j;hchar=non_char;cur_rh=non_char; + goto resume; + } + else{@+if (hchar < non_char) if (odd(hyf[j])) + {@+hyphen_passed=j;hchar=non_char; + } + if (op_byte(q) < kern_flag) + @<Carry out a ligature replacement, updating the cursor structure and +possibly advancing~|j|; |goto resume| if the cursor doesn't advance, otherwise +|goto done|@>; + w=char_kern(hf, q);goto done; /*this kern will be inserted below*/ + } + if (skip_byte(q) >= stop_flag) + if (cur_rh==non_char) goto done; + else{@+cur_rh=non_char;goto resume; + } + k=k+qo(skip_byte(q))+1;q=font_info[k].qqqq; + } +done: + +@ @d wrap_lig(A) if (ligature_present) + {@+p=new_ligature(hf, cur_l, link(cur_q)); + if (lft_hit) + {@+subtype(p)=2;lft_hit=false; + } + if (A) if (lig_stack==null) + {@+incr(subtype(p));rt_hit=false; + } + link(cur_q)=p;t=p;ligature_present=false; + } +@d pop_lig_stack {@+if (lig_ptr(lig_stack) > null) + {@+link(t)=lig_ptr(lig_stack); /*this is a charnode for |hu[j+1]|*/ + t=link(t);incr(j); + } + p=lig_stack;lig_stack=link(p);free_node(p, small_node_size); + if (lig_stack==null) set_cur_r@;@+else cur_r=character(lig_stack); + } /*if |lig_stack| isn't |null| we have |cur_rh==non_char|*/ + +@<Append a ligature and/or kern to the translation...@>= +wrap_lig(rt_hit); +if (w!=0) + {@+link(t)=new_kern(w);t=link(t);w=0; + } +if (lig_stack > null) + {@+cur_q=t;cur_l=character(lig_stack);ligature_present=true; + pop_lig_stack;goto resume; + } + +@ @<Carry out a ligature replacement, updating the cursor structure...@>= +{@+if (cur_l==non_char) lft_hit=true; +if (j==n) if (lig_stack==null) rt_hit=true; +check_interrupt; /*allow a way out in case there's an infinite ligature loop*/ +switch (op_byte(q)) { +case qi(1): case qi(5): {@+cur_l=rem_byte(q); /*\.{=:\@@}, \.{=:\@@>}*/ + ligature_present=true; + } @+break; +case qi(2): case qi(6): {@+cur_r=rem_byte(q); /*\.{\@@=:}, \.{\@@=:>}*/ + if (lig_stack > null) character(lig_stack)=cur_r; + else{@+lig_stack=new_lig_item(cur_r); + if (j==n) bchar=non_char; + else{@+p=get_avail();lig_ptr(lig_stack)=p; + character(p)=qi(hu[j+1]);font(p)=hf; + } + } + } @+break; +case qi(3): {@+cur_r=rem_byte(q); /*\.{\@@=:\@@}*/ + p=lig_stack;lig_stack=new_lig_item(cur_r);link(lig_stack)=p; + } @+break; +case qi(7): case qi(11): {@+wrap_lig(false); /*\.{\@@=:\@@>}, \.{\@@=:\@@>>}*/ + cur_q=t;cur_l=rem_byte(q);ligature_present=true; + } @+break; +default:{@+cur_l=rem_byte(q);ligature_present=true; /*\.{=:}*/ + if (lig_stack > null) pop_lig_stack@; + else if (j==n) goto done; + else{@+append_charnode_to_t(cur_r);incr(j);set_cur_r; + } + } +} +if (op_byte(q) > qi(4)) if (op_byte(q)!=qi(7)) goto done; +goto resume; +} + +@ Okay, we're ready to insert the potential hyphenations that were found. +When the following program is executed, we want to append the word +|hu[1 dotdot hn]| after node |ha|, and node |q| should be appended to the result. +During this process, the variable |i| will be a temporary +index into |hu|; the variable |j| will be an index to our current position +in |hu|; the variable |l| will be the counterpart of |j|, in a discretionary +branch; the variable |r| will point to new nodes being created; and +we need a few new local variables: + +@<Local variables for hyph...@>= +pointer @!major_tail, @!minor_tail; /*the end of lists in the main and + discretionary branches being reconstructed*/ +ASCII_code @!c; /*character temporarily replaced by a hyphen*/ +int @!c_loc; /*where that character came from*/ +int @!r_count; /*replacement count for discretionary*/ +pointer @!hyf_node; /*the hyphen, if it exists*/ + +@ When the following code is performed, |hyf[0]| and |hyf[hn]| will be zero. + +@<Reconstitute nodes for the hyphenated word...@>= +@/do@+{l=j;j=reconstitute(j, hn, bchar, qi(hyf_char))+1; +if (hyphen_passed==0) + {@+link(s)=link(hold_head); + while (link(s) > null) s=link(s); + if (odd(hyf[j-1])) + {@+l=j;hyphen_passed=j-1;link(hold_head)=null; + } + } +if (hyphen_passed > 0) + @<Create and append a discretionary node as an alternative to the unhyphenated +word, and continue to develop both branches until they become equivalent@>; +}@+ while (!(j > hn)); +link(s)=q + +@ In this repeat loop we will insert another discretionary if |hyf[j-1]| is +odd, when both branches of the previous discretionary end at position |j-1|. +Strictly speaking, we aren't justified in doing this, because we don't know +that a hyphen after |j-1| is truly independent of those branches. But in almost +all applications we would rather not lose a potentially valuable hyphenation +point. (Consider the word `difficult', where the letter `c' is in position |j|.) + +@d advance_major_tail {@+major_tail=link(major_tail);incr(r_count); + } + +@<Create and append a discretionary node as an alternative...@>= +@/do@+{r=get_node(small_node_size); +link(r)=link(hold_head);type(r)=disc_node; +major_tail=r;r_count=0; +while (link(major_tail) > null) advance_major_tail; +i=hyphen_passed;hyf[i]=0; +@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>; +@<Put the \(c)characters |hu[i+1..]| into |post_break(r)|, appending to this +list and to |major_tail| until synchronization has been achieved@>; +@<Move pointer |s| to the end of the current list, and set |replace_count(r)| +appropriately@>; +hyphen_passed=j-1;link(hold_head)=null; +}@+ while (!(!odd(hyf[j-1]))) + +@ The new hyphen might combine with the previous character via ligature +or kern. At this point we have |l-1 <= i < j| and |i < hn|. + +@<Put the \(c)characters |hu[l..i]| and a hyphen into |pre_break(r)|@>= +minor_tail=null;pre_break(r)=null;hyf_node=new_character(hf, hyf_char); +if (hyf_node!=null) + {@+incr(i);c=hu[i];hu[i]=hyf_char;free_avail(hyf_node); + } +while (l <= i) + {@+l=reconstitute(l, i, font_bchar[hf], non_char)+1; + if (link(hold_head) > null) + {@+if (minor_tail==null) pre_break(r)=link(hold_head); + else link(minor_tail)=link(hold_head); + minor_tail=link(hold_head); + while (link(minor_tail) > null) minor_tail=link(minor_tail); + } + } +if (hyf_node!=null) + {@+hu[i]=c; /*restore the character in the hyphen position*/ + l=i;decr(i); + } + +@ The synchronization algorithm begins with |l==i+1 <= j|. + +@<Put the \(c)characters |hu[i+1..]| into |post_break(r)|...@>= +minor_tail=null;post_break(r)=null;c_loc=0; +if (bchar_label[hf]!=non_address) /*put left boundary at beginning of new line*/ + {@+decr(l);c=hu[l];c_loc=l;hu[l]=256; + } +while (l < j) + {@+@/do@+{l=reconstitute(l, hn, bchar, non_char)+1; + if (c_loc > 0) + {@+hu[c_loc]=c;c_loc=0; + } + if (link(hold_head) > null) + {@+if (minor_tail==null) post_break(r)=link(hold_head); + else link(minor_tail)=link(hold_head); + minor_tail=link(hold_head); + while (link(minor_tail) > null) minor_tail=link(minor_tail); + } + }@+ while (!(l >= j)); + while (l > j) + @<Append characters of |hu[j..]| to |major_tail|, advancing~|j|@>; + } + +@ @<Append characters of |hu[j..]|...@>= +{@+j=reconstitute(j, hn, bchar, non_char)+1; +link(major_tail)=link(hold_head); +while (link(major_tail) > null) advance_major_tail; +} + +@ Ligature insertion can cause a word to grow exponentially in size. Therefore +we must test the size of |r_count| here, even though the hyphenated text +was at most 63 characters long. + +@<Move pointer |s| to the end of the current list...@>= +if (r_count > 127) /*we have to forget the discretionary hyphen*/ + {@+link(s)=link(r);link(r)=null;flush_node_list(r); + } +else{@+link(s)=r;replace_count(r)=r_count; + } +s=major_tail + +@* Hyphenation. +When a word |hc[1 dotdot hn]| has been set up to contain a candidate for hyphenation, +\TeX\ first looks to see if it is in the user's exception dictionary. If not, +hyphens are inserted based on patterns that appear within the given word, +using an algorithm due to Frank~M. Liang. +@^Liang, Franklin Mark@> + +Let's consider Liang's method first, since it is much more interesting than the +exception-lookup routine. The algorithm begins by setting |hyf[j]| to zero +for all |j|, and invalid characters are inserted into |hc[0]| +and |hc[hn+1]| to serve as delimiters. Then a reasonably fast method is +used to see which of a given set of patterns occurs in the word +|hc[0 dotdot(hn+1)]|. Each pattern $p_1\ldots p_k$ of length |k| has an associated +sequence of |k+1| numbers $n_0\ldots n_k$; and if the pattern occurs in +|hc[(j+1)dotdot(j+k)]|, \TeX\ will set |hyf[j+i]=@tmax@>(hyf[j+i],@t$n\_i$@>)| for +|0 <= i <= k|. After this has been done for each pattern that occurs, a +discretionary hyphen will be inserted between |hc[j]| and |hc[j+1]| when +|hyf[j]| is odd, as we have already seen. + +The set of patterns $p_1\ldots p_k$ and associated numbers $n_0\ldots n_k$ +depends, of course, on the language whose words are being hyphenated, and +on the degree of hyphenation that is desired. A method for finding +appropriate |p|'s and |n|'s, from a given dictionary of words and acceptable +hyphenations, is discussed in Liang's Ph.D. thesis (Stanford University, +1983); \TeX\ simply starts with the patterns and works from there. + +@ The patterns are stored in a compact table that is also efficient for +retrieval, using a variant of ``trie memory'' [cf.\ {\sl The Art of +Computer Programming \bf3} (1973), 481--505]. We can find each pattern +$p_1\ldots p_k$ by letting $z_0$ be one greater than the relevant language +index and then, for |1 <= i <= k|, +setting |@t$z\_i$@>=trie_link@t$(z\_{i-1})+p\_i$@>|; the pattern will be +identified by the number $z_k$. Since all the pattern information is +packed together into a single |trie_link| array, it is necessary to +prevent confusion between the data from inequivalent patterns, so another +table is provided such that |trie_char@t$(z\_i)=p\_i$@>| for all |i|. There +is also a table |trie_op|$(z_k)$ to identify the numbers $n_0\ldots n_k$ +associated with $p_1\ldots p_k$. + +Comparatively few different number sequences $n_0\ldots n_k$ actually occur, +since most of the |n|'s are generally zero. Therefore the number sequences +are encoded in such a way that |trie_op|$(z_k)$ is only one byte long. +If |trie_op(@t$z\_k$@>)!=min_quarterword|, when $p_1\ldots p_k$ has matched +the letters in |hc[(l-k+1)dotdot l@,]| of language |t|, +we perform all of the required operations +for this pattern by carrying out the following little program: Set +|v=trie_op(@t$z\_k$@>)|. Then set |v=v+op_start[t]|, +|hyf[l-hyf_distance[v]]=@tmax@>(hyf[l-hyf_distance[v]], hyf_num[v])|, +and |v=hyf_next[v]|; repeat, if necessary, until |v==min_quarterword|. + +@<Types...@>= +typedef int32_t trie_pointer; /*an index into |trie|*/ + +@ @d trie_link(A) trie[A].rh /*``downward'' link in a trie*/ +@d trie_char(A) trie[A].b1 /*character matched at this trie location*/ +@d trie_op(A) trie[A].b0 /*program for hyphenation at this trie location*/ + +@<Glob...@>= +static two_halves @!trie[trie_size+1]; /*|trie_link|, |trie_char|, |trie_op|*/ +static small_number @!hyf_distance0[trie_op_size], + *const @!hyf_distance = @!hyf_distance0-1; /*position |k-j| of $n_j$*/ +static small_number @!hyf_num0[trie_op_size], + *const @!hyf_num = @!hyf_num0-1; /*value of $n_j$*/ +static quarterword @!hyf_next0[trie_op_size], + *const @!hyf_next = @!hyf_next0-1; /*continuation code*/ +static uint16_t @!op_start[256]; /*offset for current language*/ + +@ @<Local variables for hyph...@>= +trie_pointer @!z; /*an index into |trie|*/ +int @!v; /*an index into |hyf_distance|, etc.*/ + +@ Assuming that these auxiliary tables have been set up properly, the +hyphenation algorithm is quite short. In the following code we set |hc[hn+2]| +to the impossible value 256, in order to guarantee that |hc[hn+3]| will +never be fetched. + +@<Find hyphen locations for the word in |hc|...@>= +for (j=0; j<=hn; j++) hyf[j]=0; +@<Look for the word |hc[1..hn]| in the exception table, and |goto found| (with +|hyf| containing the hyphens) if an entry is found@>; +if (trie_char(cur_lang+1)!=qi(cur_lang)) return; /*no patterns for |cur_lang|*/ +hc[0]=0;hc[hn+1]=0;hc[hn+2]=256; /*insert delimiters*/ +for (j=0; j<=hn-r_hyf+1; j++) + {@+z=trie_link(cur_lang+1)+hc[j];l=j; + while (hc[l]==qo(trie_char(z))) + {@+if (trie_op(z)!=min_quarterword) + @<Store \(m)maximum values in the |hyf| table@>; + incr(l);z=trie_link(z)+hc[l]; + } + } +found: for (j=0; j<=l_hyf-1; j++) hyf[j]=0; +for (j=0; j<=r_hyf-1; j++) hyf[hn-j]=0 + +@ @<Store \(m)maximum values in the |hyf| table@>= +{@+v=trie_op(z); +@/do@+{v=v+op_start[cur_lang];i=l-hyf_distance[v]; +if (hyf_num[v] > hyf[i]) hyf[i]=hyf_num[v]; +v=hyf_next[v]; +}@+ while (!(v==min_quarterword)); +} + +@ The exception table that is built by \TeX's \.{\\hyphenation} primitive is +organized as an ordered hash table [cf.\ Amble and Knuth, {\sl The Computer +@^Amble, Ole@> @^Knuth, Donald Ervin@> +Journal\/ \bf17} (1974), 135--142] using linear probing. If $\alpha$ and +$\beta$ are words, we will say that $\alpha<\beta$ if $\vert\alpha\vert< +\vert\beta\vert$ or if $\vert\alpha\vert=\vert\beta\vert$ and +$\alpha$ is lexicographically smaller than $\beta$. (The notation $\vert +\alpha\vert$ stands for the length of $\alpha$.) The idea of ordered hashing +is to arrange the table so that a given word $\alpha$ can be sought by computing +a hash address $h=h(\alpha)$ and then looking in table positions |h|, |h-1|, +\dots, until encountering the first word $\le\alpha$. If this word is +different from $\alpha$, we can conclude that $\alpha$ is not in the table. + +The words in the table point to lists in |mem| that specify hyphen positions +in their |info| fields. The list for $c_1\ldots c_n$ contains the number |k| if +the word $c_1\ldots c_n$ has a discretionary hyphen between $c_k$ and +$c_{k+1}$. + +@<Types...@>= +typedef int16_t hyph_pointer; /*an index into the ordered hash table*/ + +@ @<Glob...@>= +static str_number @!hyph_word[hyph_size+1]; /*exception words*/ +static pointer @!hyph_list[hyph_size+1]; /*lists of hyphen positions*/ +static hyph_pointer @!hyph_count; /*the number of words in the exception dictionary*/ + +@ @<Local variables for init...@>= +int @!z; /*runs through the exception dictionary*/ + +@ @<Set init...@>= +for (z=0; z<=hyph_size; z++) + {@+hyph_word[z]=0;hyph_list[z]=null; + } +hyph_count=0; + +@ The algorithm for exception lookup is quite simple, as soon as we have +a few more local variables to work with. + +@<Local variables for hyph...@>= +hyph_pointer @!h; /*an index into |hyph_word| and |hyph_list|*/ +str_number @!k; /*an index into |str_start|*/ +pool_pointer @!u; /*an index into |str_pool|*/ + +@ First we compute the hash code |h|, then we search until we either +find the word or we don't. Words from different languages are kept +separate by appending the language code to the string. + +@<Look for the word |hc[1...@>= +h=hc[1];incr(hn);hc[hn]=cur_lang; +for (j=2; j<=hn; j++) h=(h+h+hc[j])%hyph_size; +loop@+{@+@<If the string |hyph_word[h]| is less than \(hc)|hc[1..hn]|, |goto +not_found|; but if the two strings are equal, set |hyf| to the hyphen positions +and |goto found|@>; + if (h > 0) decr(h);@+else h=hyph_size; + } +not_found: decr(hn) + +@ @<If the string |hyph_word[h]| is less than \(hc)...@>= +k=hyph_word[h];if (k==0) goto not_found; +if (length(k) < hn) goto not_found; +if (length(k)==hn) + {@+j=1;u=str_start[k]; + @/do@+{if (so(str_pool[u]) < hc[j]) goto not_found; + if (so(str_pool[u]) > hc[j]) goto done; + incr(j);incr(u); + }@+ while (!(j > hn)); + @<Insert hyphens as specified in |hyph_list[h]|@>; + decr(hn);goto found; + } +done: + +@ @<Insert hyphens as specified...@>= +s=hyph_list[h]; +while (s!=null) + {@+hyf[info(s)]=1;s=link(s); + } + +@ @<Search |hyph_list| for pointers to |p|@>= +for (q=0; q<=hyph_size; q++) + {@+if (hyph_list[q]==p) + {@+print_nl("HYPH(");print_int(q);print_char(')'); + } + } + +@ We have now completed the hyphenation routine, so the |line_break| procedure +is finished at last. Since the hyphenation exception table is fresh in our +minds, it's a good time to deal with the routine that adds new entries to it. + +When \TeX\ has scanned `\.{\\hyphenation}', it calls on a procedure named +|new_hyph_exceptions| to do the right thing. + +@d set_cur_lang if (language <= 0) cur_lang=0; + else if (language > 255) cur_lang=0; + else cur_lang=language + +@p static void new_hyph_exceptions(void) /*enters new exceptions*/ +{@+ +int n; /*length of current word; not always a |small_number|*/ +int @!j; /*an index into |hc|*/ +hyph_pointer @!h; /*an index into |hyph_word| and |hyph_list|*/ +str_number @!k; /*an index into |str_start|*/ +pointer @!p; /*head of a list of hyphen positions*/ +pointer @!q; /*used when creating a new node for list |p|*/ +str_number @!s, @!t; /*strings being compared or stored*/ +pool_pointer @!u, @!v; /*indices into |str_pool|*/ +scan_left_brace(); /*a left brace must follow \.{\\hyphenation}*/ +set_cur_lang; +#ifdef @!INIT +if (trie_not_ready) + {@+hyph_index=0;goto not_found1; + } +#endif +set_hyph_index; +not_found1: +@<Enter as many hyphenation exceptions as are listed, until coming to a right +brace; then |return|@>; +} + +@ @<Enter as many...@>= +n=0;p=null; +loop@+{@+get_x_token(); + reswitch: switch (cur_cmd) { + case letter: case other_char: case char_given: @<Append a new letter or +hyphen@>@;@+break; + case char_num: {@+scan_char_num();cur_chr=cur_val;cur_cmd=char_given; + goto reswitch; + } + case spacer: case right_brace: {@+if (n > 1) @<Enter a hyphenation exception@>; + if (cur_cmd==right_brace) return; + n=0;p=null; + } @+break; + default:@<Give improper \.{\\hyphenation} error@>@; + } + } + +@ @<Give improper \.{\\hyph...@>= +{@+print_err("Improper ");print_esc("hyphenation"); +@.Improper \\hyphenation...@> + print(" will be flushed"); +help2("Hyphenation exceptions must contain only letters",@/ + "and hyphens. But continue; I'll forgive and forget."); +error(); +} + +@ @<Append a new letter or hyphen@>= +if (cur_chr=='-') @<Append the value |n| to list |p|@>@; +else{@+set_lc_code(cur_chr); + if (hc[0]==0) + {@+print_err("Not a letter"); +@.Not a letter@> + help2("Letters in \\hyphenation words must have \\lccode>0.",@/ + "Proceed; I'll ignore the character I just read."); + error(); + } + else if (n < 63) + {@+incr(n);hc[n]=hc[0]; + } + } + +@ @<Append the value |n| to list |p|@>= +{@+if (n < 63) + {@+q=get_avail();link(q)=p;info(q)=n;p=q; + } +} + +@ @<Enter a hyphenation exception@>= +{@+incr(n);hc[n]=cur_lang;str_room(n);h=0; +for (j=1; j<=n; j++) + {@+h=(h+h+hc[j])%hyph_size; + append_char(hc[j]); + } +s=make_string(); +@<Insert the \(p)pair |(s,p)| into the exception table@>; +} + +@ @<Insert the \(p)pair |(s,p)|...@>= +if (hyph_count==hyph_size) overflow("exception dictionary", hyph_size); +@:TeX capacity exceeded exception dictionary}{\quad exception dictionary@> +incr(hyph_count); +while (hyph_word[h]!=0) + {@+@<If the string |hyph_word[h]| is less than \(or)or equal to |s|, interchange +|(hyph_word[h],hyph_list[h])| with |(s,p)|@>; + if (h > 0) decr(h);@+else h=hyph_size; + } +hyph_word[h]=s;hyph_list[h]=p + +@ @<If the string |hyph_word[h]| is less than \(or)...@>= +k=hyph_word[h]; +if (length(k) < length(s)) goto found; +if (length(k) > length(s)) goto not_found; +u=str_start[k];v=str_start[s]; +@/do@+{if (str_pool[u] < str_pool[v]) goto found; +if (str_pool[u] > str_pool[v]) goto not_found; +incr(u);incr(v); +}@+ while (!(u==str_start[k+1])); +found: q=hyph_list[h];hyph_list[h]=p;p=q;@/ +t=hyph_word[h];hyph_word[h]=s;s=t; +not_found: + +@* Initializing the hyphenation tables. +The trie for \TeX's hyphenation algorithm is built from a sequence of +patterns following a \.{\\patterns} specification. Such a specification +is allowed only in \.{INITEX}, since the extra memory for auxiliary tables +and for the initialization program itself would only clutter up the +production version of \TeX\ with a lot of deadwood. + +The first step is to build a trie that is linked, instead of packed +into sequential storage, so that insertions are readily made. +After all patterns have been processed, \.{INITEX} +compresses the linked trie by identifying common subtries. Finally the +trie is packed into the efficient sequential form that the hyphenation +algorithm actually uses. + +@<Declare subprocedures for |line_break|@>= +#ifdef @!INIT +@<Declare procedures for preprocessing hyphenation patterns@>@; +#endif + +@ Before we discuss trie building in detail, let's consider the simpler +problem of creating the |hyf_distance|, |hyf_num|, and |hyf_next| arrays. + +Suppose, for example, that \TeX\ reads the pattern `\.{ab2cde1}'. This is +a pattern of length 5, with $n_0\ldots n_5=0\,0\,2\,0\,0\,1$ in the +notation above. We want the corresponding |trie_op| code |v| to have +|hyf_distance[v]==3|, |hyf_num[v]==2|, and |hyf_next[v]==@t$v^\prime$@>|, +where the auxiliary |trie_op| code $v^\prime$ has +|hyf_distance[@t$v^\prime$@>]==0|, |hyf_num[@t$v^\prime$@>]==1|, and +|hyf_next[@t$v^\prime$@>]==min_quarterword|. + +\TeX\ computes an appropriate value |v| with the |new_trie_op| subroutine +below, by setting +$$\hbox{|@t$v^\prime$@>=new_trie_op(0, 1, min_quarterword)|,\qquad +|v=new_trie_op(3, 2,@t$v^\prime$@>)|.}$$ +This subroutine looks up its three +parameters in a special hash table, assigning a new value only if these +three have not appeared before for the current language. + +The hash table is called |trie_op_hash|, and the number of entries it contains +is |trie_op_ptr|. + +@<Glob...@>= +#ifdef @!INIT +static uint16_t @!trie_op_hash0[trie_op_size+trie_op_size+1], + *const @!trie_op_hash = @!trie_op_hash0+trie_op_size; + /*trie op codes for quadruples*/ +static quarterword @!trie_used[256]; + /*largest opcode used so far for this language*/ +static ASCII_code @!trie_op_lang0[trie_op_size], + *const @!trie_op_lang = @!trie_op_lang0-1; + /*language part of a hashed quadruple*/ +static quarterword @!trie_op_val0[trie_op_size], + *const @!trie_op_val = @!trie_op_val0-1; + /*opcode corresponding to a hashed quadruple*/ +static int @!trie_op_ptr; /*number of stored ops so far*/ +#endif + +@ It's tempting to remove the |overflow| stops in the following procedure; +|new_trie_op| could return |min_quarterword| (thereby simply ignoring +part of a hyphenation pattern) instead of aborting the job. However, that would +lead to different hyphenation results on different installations of \TeX\ +using the same patterns. The |overflow| stops are necessary for portability +of patterns. + +@<Declare procedures for preprocessing hyph...@>= +static quarterword new_trie_op(small_number @!d, small_number @!n, quarterword @!v) +{@+ +int h; /*trial hash location*/ +quarterword @!u; /*trial op code*/ +int @!l; /*pointer to stored data*/ +h=abs(n+313*d+361*v+1009*cur_lang)%(trie_op_size+trie_op_size) + -trie_op_size; +loop@+{@+l=trie_op_hash[h]; + if (l==0) /*empty position found for a new op*/ + {@+if (trie_op_ptr==trie_op_size) + overflow("pattern memory ops", trie_op_size); + u=trie_used[cur_lang]; + if (u==max_quarterword) + overflow("pattern memory ops per language", + max_quarterword-min_quarterword); + incr(trie_op_ptr);incr(u);trie_used[cur_lang]=u; + hyf_distance[trie_op_ptr]=d; + hyf_num[trie_op_ptr]=n;hyf_next[trie_op_ptr]=v; + trie_op_lang[trie_op_ptr]=cur_lang;trie_op_hash[h]=trie_op_ptr; + trie_op_val[trie_op_ptr]=u;return u; + } + if ((hyf_distance[l]==d)&&(hyf_num[l]==n)&&(hyf_next[l]==v) + &&(trie_op_lang[l]==cur_lang)) + {@+return trie_op_val[l]; + } + if (h > -trie_op_size) decr(h);@+else h=trie_op_size; + } +} + +@ After |new_trie_op| has compressed the necessary opcode information, +plenty of information is available to unscramble the data into the +final form needed by our hyphenation algorithm. + +@<Sort \(t)the hyphenation op tables into proper order@>= +op_start[0]=-min_quarterword; +for (j=1; j<=255; j++) op_start[j]=op_start[j-1]+qo(trie_used[j-1]); +for (j=1; j<=trie_op_ptr; j++) + trie_op_hash[j]=op_start[trie_op_lang[j]]+trie_op_val[j]; /*destination*/ +for (j=1; j<=trie_op_ptr; j++) while (trie_op_hash[j] > j) + {@+k=trie_op_hash[j];@/ + t=hyf_distance[k];hyf_distance[k]=hyf_distance[j];hyf_distance[j]=t;@/ + t=hyf_num[k];hyf_num[k]=hyf_num[j];hyf_num[j]=t;@/ + t=hyf_next[k];hyf_next[k]=hyf_next[j];hyf_next[j]=t;@/ + trie_op_hash[j]=trie_op_hash[k];trie_op_hash[k]=k; + } + +@ Before we forget how to initialize the data structures that have been +mentioned so far, let's write down the code that gets them started. + +@<Initialize table entries...@>= +for (k=-trie_op_size; k<=trie_op_size; k++) trie_op_hash[k]=0; +for (k=0; k<=255; k++) trie_used[k]=min_quarterword; +trie_op_ptr=0; + +@ The linked trie that is used to preprocess hyphenation patterns appears +in several global arrays. Each node represents an instruction of the form +``if you see character |c|, then perform operation |o|, move to the +next character, and go to node |l|; otherwise go to node |r|.'' +The four quantities |c|, |o|, |l|, and |r| are stored in four arrays +|trie_c|, |trie_o|, |trie_l|, and |trie_r|. The root of the trie +is |trie_l[0]|, and the number of nodes is |trie_ptr|. Null trie +pointers are represented by zero. To initialize the trie, we simply +set |trie_l[0]| and |trie_ptr| to zero. We also set |trie_c[0]| to some +arbitrary value, since the algorithm may access it. + +The algorithms maintain the condition +$$\hbox{|trie_c[trie_r[z]] > trie_c[z]|\qquad +whenever |z!=0| and |trie_r[z]!=0|};$$ in other words, sibling nodes are +ordered by their |c| fields. + +@d trie_root trie_l[0] /*root of the linked trie*/ + +@<Glob...@>= +#ifdef @!INIT +static packed_ASCII_code @!trie_c[trie_size+1]; + /*characters to match*/ +@t\hskip10pt@>static quarterword @!trie_o[trie_size+1]; + /*operations to perform*/ +@t\hskip10pt@>static trie_pointer @!trie_l[trie_size+1]; + /*left subtrie links*/ +@t\hskip10pt@>static trie_pointer @!trie_r[trie_size+1]; + /*right subtrie links*/ +@t\hskip10pt@>static trie_pointer @!trie_ptr; /*the number of nodes in the trie*/ +@t\hskip10pt@>static trie_pointer @!trie_hash[trie_size+1]; + /*used to identify equivalent subtries*/ +#endif + +@ Let us suppose that a linked trie has already been constructed. +Experience shows that we can often reduce its size by recognizing common +subtries; therefore another hash table is introduced for this purpose, +somewhat similar to |trie_op_hash|. The new hash table will be +initialized to zero. + +The function |trie_node(p)| returns |p| if |p| is distinct from other nodes +that it has seen, otherwise it returns the number of the first equivalent +node that it has seen. + +Notice that we might make subtries equivalent even if they correspond to +patterns for different languages, in which the trie ops might mean quite +different things. That's perfectly all right. + +@<Declare procedures for preprocessing hyph...@>= +static trie_pointer trie_node(trie_pointer @!p) /*converts + to a canonical form*/ +{@+ +trie_pointer h; /*trial hash location*/ +trie_pointer @!q; /*trial trie node*/ +h=abs(trie_c[p]+1009*trie_o[p]+@| + 2718*trie_l[p]+3142*trie_r[p])%trie_size; +loop@+{@+q=trie_hash[h]; + if (q==0) + {@+trie_hash[h]=p;return p; + } + if ((trie_c[q]==trie_c[p])&&(trie_o[q]==trie_o[p])&&@| + (trie_l[q]==trie_l[p])&&(trie_r[q]==trie_r[p])) + {@+return q; + } + if (h > 0) decr(h);@+else h=trie_size; + } +} + +@ A neat recursive procedure is now able to compress a trie by +traversing it and applying |trie_node| to its nodes in ``bottom up'' +fashion. We will compress the entire trie by clearing |trie_hash| to +zero and then saying `|trie_root=compress_trie(trie_root)|'. +@^recursion@> + +@<Declare procedures for preprocessing hyph...@>= +static trie_pointer compress_trie(trie_pointer @!p) +{@+if (p==0) return 0; +else{@+trie_l[p]=compress_trie(trie_l[p]); + trie_r[p]=compress_trie(trie_r[p]); + return trie_node(p); + } +} + +@ The compressed trie will be packed into the |trie| array using a +``top-down first-fit'' procedure. This is a little tricky, so the reader +should pay close attention: The |trie_hash| array is cleared to zero +again and renamed |trie_ref| for this phase of the operation; later on, +|trie_ref[p]| will be nonzero only if the linked trie node |p| is the +smallest character +in a family and if the characters |c| of that family have been allocated to +locations |trie_ref[p]+c| in the |trie| array. Locations of |trie| that +are in use will have |trie_link==0|, while the unused holes in |trie| +will be doubly linked with |trie_link| pointing to the next larger vacant +location and |trie_back| pointing to the next smaller one. This double +linking will have been carried out only as far as |trie_max|, where +|trie_max| is the largest index of |trie| that will be needed. +To save time at the low end of the trie, we maintain array entries +|trie_min[c]| pointing to the smallest hole that is greater than~|c|. +Another array |trie_taken| tells whether or not a given location is +equal to |trie_ref[p]| for some |p|; this array is used to ensure that +distinct nodes in the compressed trie will have distinct |trie_ref| +entries. + +@d trie_ref trie_hash /*where linked trie families go into |trie|*/ +@d trie_back(A) trie[A].lh /*backward links in |trie| holes*/ + +@<Glob...@>= +#ifdef @!INIT +static bool @!trie_taken0[trie_size], + *const @!trie_taken = @!trie_taken0-1; + /*does a family start here?*/ +@t\hskip10pt@>static trie_pointer @!trie_min[256]; + /*the first possible slot for each character*/ +@t\hskip10pt@>static trie_pointer @!trie_max; /*largest location used in |trie|*/ +@t\hskip10pt@>static bool @!trie_not_ready; /*is the trie still in linked form?*/ +#endif + +@ Each time \.{\\patterns} appears, it contributes further patterns to +the future trie, which will be built only when hyphenation is attempted or +when a format file is dumped. The boolean variable |trie_not_ready| +will change to |false| when the trie is compressed; this will disable +further patterns. + +@<Initialize table entries...@>= +trie_not_ready=true;trie_root=0;trie_c[0]=si(0);trie_ptr=0; + +@ Here is how the trie-compression data structures are initialized. +If storage is tight, it would be possible to overlap |trie_op_hash|, +|trie_op_lang|, and |trie_op_val| with |trie|, |trie_hash|, and |trie_taken|, +because we finish with the former just before we need the latter. + +@<Get ready to compress the trie@>= +@<Sort \(t)the hyphenation...@>; +for (p=0; p<=trie_size; p++) trie_hash[p]=0; +hyph_root=compress_trie(hyph_root); +trie_root=compress_trie(trie_root); /*identify equivalent subtries*/ +for (p=0; p<=trie_ptr; p++) trie_ref[p]=0; +for (p=0; p<=255; p++) trie_min[p]=p+1; +trie_link(0)=1;trie_max=0 + +@ The |first_fit| procedure finds the smallest hole |z| in |trie| such that +a trie family starting at a given node |p| will fit into vacant positions +starting at |z|. If |c==trie_c[p]|, this means that location |z-c| must +not already be taken by some other family, and that |z-c+@t$c^\prime$@>| +must be vacant for all characters $c^\prime$ in the family. The procedure +sets |trie_ref[p]| to |z-c| when the first fit has been found. + +@<Declare procedures for preprocessing hyph...@>= +static void first_fit(trie_pointer @!p) /*packs a family into |trie|*/ +{@+ +trie_pointer h; /*candidate for |trie_ref[p]|*/ +trie_pointer @!z; /*runs through holes*/ +trie_pointer @!q; /*runs through the family starting at |p|*/ +ASCII_code @!c; /*smallest character in the family*/ +trie_pointer @!l, @!r; /*left and right neighbors*/ +int @!ll; /*upper limit of |trie_min| updating*/ +c=so(trie_c[p]); +z=trie_min[c]; /*get the first conceivably good hole*/ +loop@+{@+h=z-c;@/ + @<Ensure that |trie_max>=h+256|@>; + if (trie_taken[h]) goto not_found; + @<If all characters of the family fit relative to |h|, then |goto found|,\30\ +otherwise |goto not_found|@>; + not_found: z=trie_link(z); /*move to the next hole*/ + } +found: @<Pack the family into |trie| relative to |h|@>; +} + +@ By making sure that |trie_max| is at least |h+256|, we can be sure that +|trie_max > z|, since |h==z-c|. It follows that location |trie_max| will +never be occupied in |trie|, and we will have |trie_max >= trie_link(z)|. + +@<Ensure that |trie_max>=h+256|@>= +if (trie_max < h+256) + {@+if (trie_size <= h+256) overflow("pattern memory", trie_size); +@:TeX capacity exceeded pattern memory}{\quad pattern memory@> + @/do@+{incr(trie_max);trie_taken[trie_max]=false; + trie_link(trie_max)=trie_max+1;trie_back(trie_max)=trie_max-1; + }@+ while (!(trie_max==h+256)); + } + +@ @<If all characters of the family fit relative to |h|...@>= +q=trie_r[p]; +while (q > 0) + {@+if (trie_link(h+so(trie_c[q]))==0) goto not_found; + q=trie_r[q]; + } +goto found + +@ @<Pack the family into |trie| relative to |h|@>= +trie_taken[h]=true;trie_ref[p]=h;q=p; +@/do@+{z=h+so(trie_c[q]);l=trie_back(z);r=trie_link(z); +trie_back(r)=l;trie_link(l)=r;trie_link(z)=0; +if (l < 256) + {@+if (z < 256) ll=z;@+else ll=256; + @/do@+{trie_min[l]=r;incr(l); + }@+ while (!(l==ll)); + } +q=trie_r[q]; +}@+ while (!(q==0)) + +@ To pack the entire linked trie, we use the following recursive procedure. +@^recursion@> + +@<Declare procedures for preprocessing hyph...@>= +static void trie_pack(trie_pointer @!p) /*pack subtries of a family*/ +{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/ +@/do@+{q=trie_l[p]; +if ((q > 0)&&(trie_ref[q]==0)) + {@+first_fit(q);trie_pack(q); + } +p=trie_r[p]; +}@+ while (!(p==0)); +} + +@ When the whole trie has been allocated into the sequential table, we +must go through it once again so that |trie| contains the correct +information. Null pointers in the linked trie will be represented by the +value~0, which properly implements an ``empty'' family. + +@<Move the data into |trie|@>= +h.rh=0;h.b0=min_quarterword;h.b1=min_quarterword; /*|trie_link=0|, + |trie_op=min_quarterword|, |trie_char=qi(0)|*/ +if (trie_max==0) /*no patterns were given*/ + {@+for (r=0; r<=256; r++) trie[r]=h; + trie_max=256; + } +else{@+if (hyph_root > 0) trie_fix(hyph_root); + if (trie_root > 0) trie_fix(trie_root); /*this fixes the non-holes in |trie|*/ + r=0; /*now we will zero out all the holes*/ + @/do@+{s=trie_link(r);trie[r]=h;r=s; + }@+ while (!(r > trie_max)); + } +trie_char(0)=qi('?'); /*make |trie_char(c)!=c| for all |c|*/ + +@ The fixing-up procedure is, of course, recursive. Since the linked trie +usually has overlapping subtries, the same data may be moved several +times; but that causes no harm, and at most as much work is done as it +took to build the uncompressed trie. +@^recursion@> + +@<Declare procedures for preprocessing hyph...@>= +static void trie_fix(trie_pointer @!p) /*moves |p| and its siblings into |trie|*/ +{@+trie_pointer q; /*a local variable that need not be saved on recursive calls*/ +ASCII_code @!c; /*another one that need not be saved*/ +trie_pointer @!z; /*|trie| reference; this local variable must be saved*/ +z=trie_ref[p]; +@/do@+{q=trie_l[p];c=so(trie_c[p]); +trie_link(z+c)=trie_ref[q];trie_char(z+c)=qi(c);trie_op(z+c)=trie_o[p]; +if (q > 0) trie_fix(q); +p=trie_r[p]; +}@+ while (!(p==0)); +} + +@ Now let's go back to the easier problem, of building the linked +trie. When \.{INITEX} has scanned the `\.{\\patterns}' control +sequence, it calls on |new_patterns| to do the right thing. + +@<Declare procedures for preprocessing hyph...@>= +static void new_patterns(void) /*initializes the hyphenation pattern data*/ +{@+ +int k, @!l; /*indices into |hc| and |hyf|; + not always in |small_number| range*/ +bool @!digit_sensed; /*should the next digit be treated as a letter?*/ +quarterword @!v; /*trie op code*/ +trie_pointer @!p, @!q; /*nodes of trie traversed during insertion*/ +bool @!first_child; /*is |p==trie_l[q]|?*/ +int @!c; /*character being inserted*/ +if (trie_not_ready) + {@+set_cur_lang;scan_left_brace(); /*a left brace must follow \.{\\patterns}*/ + @<Enter all of the patterns into a linked trie, until coming to a right +brace@>; + if (saving_hyph_codes > 0) + @<Store hyphenation codes for current language@>; + } +else{@+print_err("Too late for ");print_esc("patterns"); + help1("All patterns must be given before typesetting begins."); + error();link(garbage)=scan_toks(false, false);flush_list(def_ref); + } +} + +@ Novices are not supposed to be using \.{\\patterns}, so the error +messages are terse. (Note that all error messages appear in \TeX's string +pool, even if they are used only by \.{INITEX}.) + +@<Enter all of the patterns into a linked trie...@>= +k=0;hyf[0]=0;digit_sensed=false; +loop@+{@+get_x_token(); + switch (cur_cmd) { + case letter: case other_char: @<Append a new letter or a hyphen level@>@;@+break; + case spacer: case right_brace: {@+if (k > 0) + @<Insert a new pattern into the linked trie@>; + if (cur_cmd==right_brace) goto done; + k=0;hyf[0]=0;digit_sensed=false; + } @+break; + default:{@+print_err("Bad ");print_esc("patterns"); +@.Bad \\patterns@> + help1("(See Appendix H.)");error(); + } + } + } +done: + +@ @<Append a new letter or a hyphen level@>= +if (digit_sensed||(cur_chr < '0')||(cur_chr > '9')) + {@+if (cur_chr=='.') cur_chr=0; /*edge-of-word delimiter*/ + else{@+cur_chr=lc_code(cur_chr); + if (cur_chr==0) + {@+print_err("Nonletter"); +@.Nonletter@> + help1("(See Appendix H.)");error(); + } + } + if (k < 63) + {@+incr(k);hc[k]=cur_chr;hyf[k]=0;digit_sensed=false; + } + } +else if (k < 63) + {@+hyf[k]=cur_chr-'0';digit_sensed=true; + } + +@ When the following code comes into play, the pattern $p_1\ldots p_k$ +appears in |hc[1 dotdot k]|, and the corresponding sequence of numbers $n_0\ldots +n_k$ appears in |hyf[0 dotdot k]|. + +@<Insert a new pattern into the linked trie@>= +{@+@<Compute the trie op code, |v|, and set |l:=0|@>; +q=0;hc[0]=cur_lang; +while (l <= k) + {@+c=hc[l];incr(l);p=trie_l[q];first_child=true; + while ((p > 0)&&(c > so(trie_c[p]))) + {@+q=p;p=trie_r[q];first_child=false; + } + if ((p==0)||(c < so(trie_c[p]))) + @<Insert a new trie node between |q| and |p|, and make |p| point to it@>; + q=p; /*now node |q| represents $p_1\ldots p_{l-1}$*/ + } +if (trie_o[q]!=min_quarterword) + {@+print_err("Duplicate pattern"); +@.Duplicate pattern@> + help1("(See Appendix H.)");error(); + } +trie_o[q]=v; +} + +@ @<Insert a new trie node between |q| and |p|...@>= +{@+if (trie_ptr==trie_size) overflow("pattern memory", trie_size); +@:TeX capacity exceeded pattern memory}{\quad pattern memory@> +incr(trie_ptr);trie_r[trie_ptr]=p;p=trie_ptr;trie_l[p]=0; +if (first_child) trie_l[q]=p;@+else trie_r[q]=p; +trie_c[p]=si(c);trie_o[p]=min_quarterword; +} + +@ @<Compute the trie op code, |v|...@>= +if (hc[1]==0) hyf[0]=0; +if (hc[k]==0) hyf[k]=0; +l=k;v=min_quarterword; +loop@+{@+if (hyf[l]!=0) v=new_trie_op(k-l, hyf[l], v); + if (l > 0) decr(l);@+else goto done1; + } +done1: + +@ Finally we put everything together: Here is how the trie gets to its +final, efficient form. +The following packing routine is rigged so that the root of the linked +tree gets mapped into location 1 of |trie|, as required by the hyphenation +algorithm. This happens because the first call of |first_fit| will +``take'' location~1. + +@<Declare procedures for preprocessing hyphenation patterns@>= +static void init_trie(void) +{@+int @!p; /*pointer for initialization*/ +int @!j, @!k, @!t; /*all-purpose registers for initialization*/ +int @!r, @!s; /*used to clean up the packed |trie|*/ +two_halves @!h; /*template used to zero out |trie|'s holes*/ +@<Local variables to save the profiling context@>@; +@<Charge the time used here on |init_trie|@>@; +@<Get ready to compress the trie@>; +if (trie_root!=0) + {@+first_fit(trie_root);trie_pack(trie_root); + } +if (hyph_root!=0) @<Pack all stored |hyph_codes|@>; +@<Move the data into |trie|@>; +trie_not_ready=false; +@<restore the previous current file, line, and command@>@; +} + +@* Breaking vertical lists into pages. +The |vsplit| procedure, which implements \TeX's \.{\\vsplit} operation, +is considerably simpler than |line_break| because it doesn't have to +worry about hyphenation, and because its mission is to discover a single +break instead of an optimum sequence of breakpoints. But before we get +into the details of |vsplit|, we need to consider a few more basic things. + +@ A subroutine called |prune_page_top| takes a pointer to a vlist and +returns a pointer to a modified vlist in which all glue, kern, and penalty nodes +have been deleted before the first box or rule node. However, the first +box or rule is actually preceded by a newly created glue node designed so that +the topmost baseline will be at distance |split_top_skip| from the top, +whenever this is possible without backspacing. + +When the second argument |s| is |false| the deleted nodes are destroyed, +otherwise they are collected in a list starting at |split_disc|. + +In this routine and those that follow, we make use of the fact that a +vertical list contains no character nodes, hence the |type| field exists +for each node in the list. +@^data structure assumptions@> + +@p static pointer prune_page_top(pointer @!p, bool @!s) + /*adjust top after page break*/ +{@+pointer prev_p; /*lags one step behind |p|*/ +pointer @!q, @!r; /*temporary variables for list manipulation*/ +prev_p=temp_head;link(temp_head)=p; +while (p!=null) + switch (type(p)) { + case hlist_node: case vlist_node: case rule_node: @<Insert glue for |split_top_skip| +and set~|p:=null|@>@;@+break; + case whatsit_node: case mark_node: case ins_node: {@+prev_p=p;p=link(prev_p); + } @+break; + case glue_node: case kern_node: case penalty_node: {@+q=p;p=link(q);link(q)=null; + link(prev_p)=p; + if (s) + {@+if (split_disc==null) split_disc=q;@+else link(r)=q; + r=q; + } + else flush_node_list(q); + } @+break; + default:confusion("pruning"); +@:this can't happen pruning}{\quad pruning@> + } +return link(temp_head); +} + +@ @<Insert glue for |split_top_skip|...@>= +{@+q=new_skip_param(split_top_skip_code);link(prev_p)=q;link(q)=p; + /*now |temp_ptr==glue_ptr(q)|*/ +if (width(temp_ptr) > height(p)) width(temp_ptr)=width(temp_ptr)-height(p); +else width(temp_ptr)=0; +p=null; +} + +@ The next subroutine finds the best place to break a given vertical list +so as to obtain a box of height~|h|, with maximum depth~|d|. +A pointer to the beginning of the vertical list is given, +and a pointer to the optimum breakpoint is returned. The list is effectively +followed by a forced break, i.e., a penalty node with the |eject_penalty|; +if the best break occurs at this artificial node, the value |null| is returned. + +An array of six |scaled| distances is used to keep track of the height +from the beginning of the list to the current place, just as in |line_break|. +In fact, we use one of the same arrays, only changing its name to reflect +its new significance. + +@d active_height active_width /*new name for the six distance variables*/ +@d cur_height active_height[1] /*the natural height*/ +@d set_height_zero(A) active_height[A]=0 /*initialize the height to zero*/ +@# +@p static pointer vert_break(pointer @!p, scaled @!h, scaled @!d) + /*finds optimum page break*/ +{@+ +pointer prev_p; /*if |p| is a glue node, |type(prev_p)| determines + whether |p| is a legal breakpoint*/ +pointer @!q, @!r; /*glue specifications*/ +int @!pi; /*penalty value*/ +int @!b; /*badness at a trial breakpoint*/ +int @!least_cost; /*the smallest badness plus penalties found so far*/ +pointer @!best_place; /*the most recent break that leads to |least_cost|*/ +scaled @!prev_dp; /*depth of previous box in the list*/ +small_number @!t; /*|type| of the node following a kern*/ +prev_p=p; /*an initial glue node is not a legal breakpoint*/ +least_cost=awful_bad;do_all_six(set_height_zero);prev_dp=0; +loop@+{@+@<If node |p| is a legal breakpoint, check if this break is the best +known, and |goto done| if |p| is null or if the page-so-far is already too +full to accept more stuff@>; + prev_p=p;p=link(prev_p); + } +done: return best_place; +} + +@ A global variable |best_height_plus_depth| will be set to the natural size +of the box that corresponds to the optimum breakpoint found by |vert_break|. +(This value is used by the insertion-splitting algorithm of the page builder.) + +@<Glob...@>= +static scaled @!best_height_plus_depth; /*height of the best box, without stretching or + shrinking*/ + +@ A subtle point to be noted here is that the maximum depth~|d| might be +negative, so |cur_height| and |prev_dp| might need to be corrected even +after a glue or kern node. + +@<If node |p| is a legal breakpoint, check...@>= +if (p==null) pi=eject_penalty; +else@<Use node |p| to update the current height and depth measurements; if +this node is not a legal breakpoint, |goto not_found| or |update_heights|, +otherwise set |pi| to the associated penalty at the break@>; +@<Check if node |p| is a new champion breakpoint; then \(go)|goto done| if +|p| is a forced break or if the page-so-far is already too full@>; +if ((type(p) < glue_node)||(type(p) > kern_node)) goto not_found; +update_heights: @<Update the current height and depth measurements with respect +to a glue or kern node~|p|@>; +not_found: if (prev_dp > d) + {@+cur_height=cur_height+prev_dp-d; + prev_dp=d; + } + +@ @<Use node |p| to update the current height and depth measurements...@>= +switch (type(p)) { +case hlist_node: case vlist_node: case rule_node: {@+@t@>@;@/ + cur_height=cur_height+prev_dp+height(p);prev_dp=depth(p); + goto not_found; + } +case whatsit_node: @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>; +case glue_node: if (precedes_break(prev_p)) pi=0; + else goto update_heights;@+break; +case kern_node: {@+if (link(p)==null) t=penalty_node; + else t=type(link(p)); + if (t==glue_node) pi=0;@+else goto update_heights; + } @+break; +case penalty_node: pi=penalty(p);@+break; +case mark_node: case ins_node: goto not_found; +default:confusion("vertbreak"); +@:this can't happen vertbreak}{\quad vertbreak@> +} + +@ @d deplorable 100000 /*more than |inf_bad|, but less than |awful_bad|*/ + +@<Check if node |p| is a new champion breakpoint; then \(go)...@>= +if (pi < inf_penalty) + {@+@<Compute the badness, |b|, using |awful_bad| if the box is too full@>; + if (b < awful_bad) + if (pi <= eject_penalty) b=pi; + else if (b < inf_bad) b=b+pi; + else b=deplorable; + if (b <= least_cost) + {@+best_place=p;least_cost=b; + best_height_plus_depth=cur_height+prev_dp; + } + if ((b==awful_bad)||(pi <= eject_penalty)) goto done; + } + +@ @<Compute the badness, |b|, using |awful_bad| if the box is too full@>= +if (cur_height < h) + if ((active_height[3]!=0)||(active_height[4]!=0)|| + (active_height[5]!=0)) b=0; + else b=badness(h-cur_height, active_height[2]); +else if (cur_height-h > active_height[6]) b=awful_bad; +else b=badness(cur_height-h, active_height[6]) + +@ Vertical lists that are subject to the |vert_break| procedure should not +contain infinite shrinkability, since that would permit any amount of +information to ``fit'' on one page. + +@<Update the current height and depth measurements with...@>= +if (type(p)==kern_node) q=p; +else{@+q=glue_ptr(p); + active_height[2+stretch_order(q)]=@| + active_height[2+stretch_order(q)]+stretch(q);@/ + active_height[6]=active_height[6]+shrink(q); + if ((shrink_order(q)!=normal)&&(shrink(q)!=0)) + {@+@t@>@;@/ + print_err("Infinite glue shrinkage found in box being split");@/ +@.Infinite glue shrinkage...@> + help4("The box you are \\vsplitting contains some infinitely",@/ + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",@/ + "Such glue doesn't belong there; but you can safely proceed,",@/ + "since the offensive shrinkability has been made finite."); + error();r=new_spec(q);shrink_order(r)=normal;delete_glue_ref(q); + glue_ptr(p)=r;q=r; + } + } +cur_height=cur_height+prev_dp+width(q);prev_dp=0 + +@ Now we are ready to consider |vsplit| itself. Most of +its work is accomplished by the two subroutines that we have just considered. + +Given the number of a vlist box |n|, and given a desired page height |h|, +the |vsplit| function finds the best initial segment of the vlist and +returns a box for a page of height~|h|. The remainder of the vlist, if +any, replaces the original box, after removing glue and penalties and +adjusting for |split_top_skip|. Mark nodes in the split-off box are used to +set the values of |split_first_mark| and |split_bot_mark|; we use the +fact that |split_first_mark==null| if and only if |split_bot_mark==null|. + +The original box becomes ``void'' if and only if it has been entirely +extracted. The extracted box is ``void'' if and only if the original +box was void (or if it was, erroneously, an hlist box). + +@p @t\4@>@<Declare the function called |do_marks|@>@; +static pointer vsplit(halfword @!n, scaled @!h) + /*extracts a page of height |h| from box |n|*/ +{@+ +pointer v; /*the box to be split*/ +pointer p; /*runs through the vlist*/ +pointer q; /*points to where the break occurs*/ +cur_val=n;fetch_box(v); +flush_node_list(split_disc);split_disc=null; +if (sa_mark!=null) + if (do_marks(vsplit_init, 0, sa_mark)) sa_mark=null; +if (split_first_mark!=null) + {@+delete_token_ref(split_first_mark);split_first_mark=null; + delete_token_ref(split_bot_mark);split_bot_mark=null; + } +@<Dispense with trivial cases of void or bad boxes@>; +q=vert_break(list_ptr(v), h, split_max_depth); +@<Look at all the marks in nodes before the break, and set the final link +to |null| at the break@>; +q=prune_page_top(q, saving_vdiscards > 0); +p=list_ptr(v);free_node(v, box_node_size); +if (q!=null) q=vpack(q, natural); +change_box(q); /*the |eq_level| of the box stays the same*/ +return vpackage(p, h, exactly, split_max_depth); +} + +@ @<Dispense with trivial cases of void or bad boxes@>= +if (v==null) + {@+return null; + } +if (type(v)!=vlist_node) + {@+print_err("");print_esc("vsplit");print(" needs a "); + print_esc("vbox"); +@:vsplit\_}{\.{\\vsplit needs a \\vbox}@> + help2("The box you are trying to split is an \\hbox.",@/ + "I can't split such a box, so I'll leave it alone."); + error();return null; + } + +@ It's possible that the box begins with a penalty node that is the +``best'' break, so we must be careful to handle this special case correctly. + +@<Look at all the marks...@>= +p=list_ptr(v); +if (p==q) list_ptr(v)=null; +else loop@+{@+if (type(p)==mark_node) + if (mark_class(p)!=0) @<Update the current marks for |vsplit|@>@; + else if (split_first_mark==null) + {@+split_first_mark=mark_ptr(p); + split_bot_mark=split_first_mark; + token_ref_count(split_first_mark)=@| + token_ref_count(split_first_mark)+2; + } + else{@+delete_token_ref(split_bot_mark); + split_bot_mark=mark_ptr(p); + add_token_ref(split_bot_mark); + } + if (link(p)==q) + {@+link(p)=null;goto done; + } + p=link(p); + } +done: + +@* The page builder. +When \TeX\ appends new material to its main vlist in vertical mode, it uses +a method something like |vsplit| to decide where a page ends, except that +the calculations are done ``on line'' as new items come in. +The main complication in this process is that insertions must be put +into their boxes and removed from the vlist, in a more-or-less optimum manner. + +We shall use the term ``current page'' for that part of the main vlist that +is being considered as a candidate for being broken off and sent to the +user's output routine. The current page starts at |link(page_head)|, and +it ends at |page_tail|. We have |page_head==page_tail| if this list is empty. +@^current page@> + +Utter chaos would reign if the user kept changing page specifications +while a page is being constructed, so the page builder keeps the pertinent +specifications frozen as soon as the page receives its first box or +insertion. The global variable |page_contents| is |empty| when the +current page contains only mark nodes and content-less whatsit nodes; it +is |inserts_only| if the page contains only insertion nodes in addition to +marks and whatsits. Glue nodes, kern nodes, and penalty nodes are +discarded until a box or rule node appears, at which time |page_contents| +changes to |box_there|. As soon as |page_contents| becomes non-|empty|, +the current |vsize| and |max_depth| are squirreled away into |page_goal| +and |page_max_depth|; the latter values will be used until the page has +been forwarded to the user's output routine. The \.{\\topskip} adjustment +is made when |page_contents| changes to |box_there|. + +Although |page_goal| starts out equal to |vsize|, it is decreased by the +scaled natural height-plus-depth of the insertions considered so far, and by +the \.{\\skip} corrections for those insertions. Therefore it represents +the size into which the non-inserted material should fit, assuming that +all insertions in the current page have been made. + +The global variables |best_page_break| and |least_page_cost| correspond +respectively to the local variables |best_place| and |least_cost| in the +|vert_break| routine that we have already studied; i.e., they record the +location and value of the best place currently known for breaking the +current page. The value of |page_goal| at the time of the best break is +stored in |best_size|. + +@d inserts_only 1 + /*|page_contents| when an insert node has been contributed, but no boxes*/ +@d box_there 2 /*|page_contents| when a box or rule has been contributed*/ + +@<Glob...@>= +static pointer @!page_tail; /*the final node on the current page*/ +static int @!page_contents; /*what is on the current page so far?*/ +static scaled @!page_max_depth; /*maximum box depth on page being built*/ +static pointer @!best_page_break; /*break here to get the best page known so far*/ +static int @!least_page_cost; /*the score for this currently best page*/ +static scaled @!best_size; /*its |page_goal|*/ + +@ The page builder has another data structure to keep track of insertions. +This is a list of four-word nodes, starting and ending at |page_ins_head|. +That is, the first element of the list is node |r@t$\_1$@>==link(page_ins_head)|; +node $r_j$ is followed by |r@t$\_{j+1}$@>==link(r@t$\_j$@>)|; and if there are +|n| items we have |r@t$\_{n+1}$@>==page_ins_head|. The |subtype| field of +each node in this list refers to an insertion number; for example, `\.{\\insert +250}' would correspond to a node whose |subtype| is |qi(250)| +(the same as the |subtype| field of the relevant |ins_node|). These |subtype| +fields are in increasing order, and |subtype(page_ins_head)== +qi(255)|, so |page_ins_head| serves as a convenient sentinel +at the end of the list. A record is present for each insertion number that +appears in the current page. + +The |type| field in these nodes distinguishes two possibilities that +might occur as we look ahead before deciding on the optimum page break. +If |type(r)==inserting|, then |height(r)| contains the total of the +height-plus-depth dimensions of the box and all its inserts seen so far. +If |type(r)==split_up|, then no more insertions will be made into this box, +because at least one previous insertion was too big to fit on the current +page; |broken_ptr(r)| points to the node where that insertion will be +split, if \TeX\ decides to split it, |broken_ins(r)| points to the +insertion node that was tentatively split, and |height(r)| includes also the +natural height plus depth of the part that would be split off. + +In both cases, |last_ins_ptr(r)| points to the last |ins_node| +encountered for box |qo(subtype(r))| that would be at least partially +inserted on the next page; and |best_ins_ptr(r)| points to the last +such |ins_node| that should actually be inserted, to get the page with +minimum badness among all page breaks considered so far. We have +|best_ins_ptr(r)==null| if and only if no insertion for this box should +be made to produce this optimum page. + +The data structure definitions here use the fact that the |@!height| field +appears in the fourth word of a box node. +@^data structure assumptions@> + +@d page_ins_node_size 4 /*number of words for a page insertion node*/ +@d inserting 0 /*an insertion class that has not yet overflowed*/ +@d split_up 1 /*an overflowed insertion class*/ +@d broken_ptr(A) link(A+1) + /*an insertion for this class will break here if anywhere*/ +@d broken_ins(A) info(A+1) /*this insertion might break at |broken_ptr|*/ +@d last_ins_ptr(A) link(A+2) /*the most recent insertion for this |subtype|*/ +@d best_ins_ptr(A) info(A+2) /*the optimum most recent insertion*/ + +@<Initialize the special list heads...@>= +subtype(page_ins_head)=qi(255); +type(page_ins_head)=split_up;link(page_ins_head)=page_ins_head; + +@ An array |page_so_far| records the heights and depths of everything +on the current page. This array contains six |scaled| numbers, like the +similar arrays already considered in |line_break| and |vert_break|; and it +also contains |page_goal| and |page_depth|, since these values are +all accessible to the user via |set_page_dimen| commands. The +value of |page_so_far[1]| is also called |page_total|. The stretch +and shrink components of the \.{\\skip} corrections for each insertion are +included in |page_so_far|, but the natural space components of these +corrections are not, since they have been subtracted from |page_goal|. + +The variable |page_depth| records the depth of the current page; it has been +adjusted so that it is at most |page_max_depth|. The variable +|last_glue| points to the glue specification of the most recent node +contributed from the contribution list, if this was a glue node; otherwise +|last_glue==max_halfword|. (If the contribution list is nonempty, +however, the value of |last_glue| is not necessarily accurate.) +The variables |last_penalty|, |last_kern|, and |last_node_type| +are similar. And +finally, |insert_penalties| holds the sum of the penalties associated with +all split and floating insertions. + +@d page_goal page_so_far[0] /*desired height of information on page being built*/ +@d page_total page_so_far[1] /*height of the current page*/ +@d page_shrink page_so_far[6] /*shrinkability of the current page*/ +@d page_depth page_so_far[7] /*depth of the current page*/ + +@<Glob...@>= +static scaled @!page_so_far[8]; /*height and glue of the current page*/ +static pointer @!last_glue; /*used to implement \.{\\lastskip}*/ +static int @!last_penalty; /*used to implement \.{\\lastpenalty}*/ +static scaled @!last_kern; /*used to implement \.{\\lastkern}*/ +static int @!last_node_type; /*used to implement \.{\\lastnodetype}*/ +static int @!insert_penalties; /*sum of the penalties for insertions + that were held over*/ + +@ @<Put each...@>= +primitive("pagegoal", set_page_dimen, 0); +@!@:page\_goal\_}{\.{\\pagegoal} primitive@> +primitive("pagetotal", set_page_dimen, 1); +@!@:page\_total\_}{\.{\\pagetotal} primitive@> +primitive("pagestretch", set_page_dimen, 2); +@!@:page\_stretch\_}{\.{\\pagestretch} primitive@> +primitive("pagefilstretch", set_page_dimen, 3); +@!@:page\_fil\_stretch\_}{\.{\\pagefilstretch} primitive@> +primitive("pagefillstretch", set_page_dimen, 4); +@!@:page\_fill\_stretch\_}{\.{\\pagefillstretch} primitive@> +primitive("pagefilllstretch", set_page_dimen, 5); +@!@:page\_filll\_stretch\_}{\.{\\pagefilllstretch} primitive@> +primitive("pageshrink", set_page_dimen, 6); +@!@:page\_shrink\_}{\.{\\pageshrink} primitive@> +primitive("pagedepth", set_page_dimen, 7); +@!@:page\_depth\_}{\.{\\pagedepth} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case set_page_dimen: switch (chr_code) { +case 0: print_esc("pagegoal");@+break; +case 1: print_esc("pagetotal");@+break; +case 2: print_esc("pagestretch");@+break; +case 3: print_esc("pagefilstretch");@+break; +case 4: print_esc("pagefillstretch");@+break; +case 5: print_esc("pagefilllstretch");@+break; +case 6: print_esc("pageshrink");@+break; +default:print_esc("pagedepth"); +} @+break; + +@ @d print_plus(A, B) if (page_so_far[A]!=0) + {@+print(" plus ");print_scaled(page_so_far[A]);print(B);@+} + +@p static void print_totals(void) +{@+print_scaled(page_total); +print_plus(2,""); +print_plus(3,"fil"); +print_plus(4,"fill"); +print_plus(5,"filll"); +if (page_shrink!=0) + {@+print(" minus ");print_scaled(page_shrink); + } +} + +@ @<Show the status of the current page@>= +if (page_head!=page_tail) + {@+print_nl("### current page:"); + if (output_active) print(" (held over for next output)"); +@.held over for next output@> + show_box(link(page_head)); + if (page_contents > empty) + {@+print_nl("total height ");print_totals(); +@:total\_height}{\.{total height}@> + print_nl(" goal height ");print_scaled(page_goal); +@.goal height@> + r=link(page_ins_head); + while (r!=page_ins_head) + {@+print_ln();print_esc("insert");t=qo(subtype(r)); + print_int(t);print(" adds "); + if (count(t)==1000) t=height(r); + else t=x_over_n(height(r), 1000)*count(t); + print_scaled(t); + if (type(r)==split_up) + {@+q=page_head;t=0; + @/do@+{q=link(q); + if ((type(q)==ins_node)&&(subtype(q)==subtype(r))) incr(t); + }@+ while (!(q==broken_ins(r))); + print(", #");print_int(t);print(" might split"); + } + r=link(r); + } + } + } + +@ Here is a procedure that is called when the |page_contents| is changing +from |empty| to |inserts_only| or |box_there|. + +@d set_page_so_far_zero(A) page_so_far[A]=0 + +@p static void freeze_page_specs(small_number @!s) +{@+page_contents=s; +page_goal=vsize;page_max_depth=max_depth; +page_depth=0;do_all_six(set_page_so_far_zero); +least_page_cost=awful_bad; +#ifdef @!STAT +if (tracing_pages > 0) + {@+begin_diagnostic(); + print_nl("%% goal height=");print_scaled(page_goal); +@.goal height@> + print(", max depth=");print_scaled(page_max_depth); + end_diagnostic(false); + } @; +#endif +@;@/ +} + +@ Pages are built by appending nodes to the current list in \TeX's +vertical mode, which is at the outermost level of the semantic nest. This +vlist is split into two parts; the ``current page'' that we have been +talking so much about already, and the ``contribution list'' that receives +new nodes as they are created. The current page contains everything that +the page builder has accounted for in its data structures, as described +above, while the contribution list contains other things that have been +generated by other parts of \TeX\ but have not yet been +seen by the page builder. +The contribution list starts at |link(contrib_head)|, and it ends at the +current node in \TeX's vertical mode. + +When \TeX\ has appended new material in vertical mode, it calls the procedure +|build_page|, which tries to catch up by moving nodes from the contribution +list to the current page. This procedure will succeed in its goal of +emptying the contribution list, unless a page break is discovered, i.e., +unless the current page has grown to the point where the optimum next +page break has been determined. In the latter case, the nodes after the +optimum break will go back onto the contribution list, and control will +effectively pass to the user's output routine. + +We make |type(page_head)==glue_node|, so that an initial glue node on +the current page will not be considered a valid breakpoint. + +@<Initialize the special list...@>= +type(page_head)=glue_node;subtype(page_head)=normal; + +@ The global variable |output_active| is true during the time the +user's output routine is driving \TeX. + +@<Glob...@>= +static bool @!output_active; /*are we in the midst of an output routine?*/ + +@ @<Set init...@>= +output_active=false;insert_penalties=0; + +@ The page builder is ready to start a fresh page if we initialize +the following state variables. (However, the page insertion list is initialized +elsewhere.) + +@<Start a new current page@>= +page_contents=empty;page_tail=page_head;link(page_head)=null;@/ +last_glue=max_halfword;last_penalty=0;last_kern=0; +last_node_type=-1; +page_depth=0;page_max_depth=0 + +@ At certain times box 255 is supposed to be void (i.e., |null|), +or an insertion box is supposed to be ready to accept a vertical list. +If not, an error message is printed, and the following subroutine +flushes the unwanted contents, reporting them to the user. + +@p static void box_error(eight_bits @!n) +{@+error();begin_diagnostic(); +print_nl("The following box has been deleted:"); +@.The following...deleted@> +show_box(box(n));end_diagnostic(true); +flush_node_list(box(n));box(n)=null; +} + +@ The following procedure guarantees that a given box register +does not contain an \.{\\hbox}. + +@p static void ensure_vbox(eight_bits @!n) +{@+pointer p; /*the box register contents*/ +p=box(n); +if (p!=null) if (type(p)==hlist_node) + {@+print_err("Insertions can only be added to a vbox"); +@.Insertions can only...@> + help3("Tut tut: You're trying to \\insert into a",@/ + "\\box register that now contains an \\hbox.",@/ + "Proceed, and I'll discard its present contents."); + box_error(n); + } +} + +@ \TeX\ is not always in vertical mode at the time |build_page| +is called; the current mode reflects what \TeX\ should return to, after +the contribution list has been emptied. A call on |build_page| should +be immediately followed by `|goto big_switch|', which is \TeX's central +control point. + +@p @t\4@>@<Declare the procedure called |fire_up|@>@;@/ +static void build_page(void) /*append contributions to the current page*/ +{@+ +pointer p; /*the node being appended*/ +pointer @!q, @!r; /*nodes being examined*/ +int @!b, @!c; /*badness and cost of current page*/ +int @!pi; /*penalty to be added to the badness*/ +int @!n; /*insertion box number*/ +scaled @!delta, @!h, @!w; /*sizes used for insertion calculations*/ +@<Local variables to save the profiling context@>@; +if ((link(contrib_head)==null)||output_active) return; +@<Charge the time used here on |build_page|@>@; +@/do@+{resume: p=link(contrib_head);@/ +@<Update the values of |last_glue|, |last_penalty|, and |last_kern|@>; +@<Move node |p| to the current page; if it is time for a page break, put the +nodes following the break back onto the contribution list, and |return| to +the user's output routine if there is one@>; +}@+ while (!(link(contrib_head)==null)); +@<Make the contribution list empty by setting its tail to |contrib_head|@>; +@<restore the previous current file, line, and command@>@; +} + +@ @d contrib_tail nest[0].tail_field /*tail of the contribution list*/ + +@<Make the contribution list empty...@>= +if (nest_ptr==0) tail=contrib_head; /*vertical mode*/ +else contrib_tail=contrib_head /*other modes*/ + +@ @<Update the values of |last_glue|...@>= +if (last_glue!=max_halfword) delete_glue_ref(last_glue); +last_penalty=0;last_kern=0; +last_node_type=type(p)+1; +if (type(p)==glue_node) + {@+last_glue=glue_ptr(p);add_glue_ref(last_glue); + } +else{@+last_glue=max_halfword; + if (type(p)==penalty_node) last_penalty=penalty(p); + else if (type(p)==kern_node) last_kern=width(p); + } + +@ The code here is an example of a many-way switch into routines that +merge together in different places. Some people call this unstructured +programming, but the author doesn't see much wrong with it, as long as +@^Knuth, Donald Ervin@> +the various labels have a well-understood meaning. + +@<Move node |p| to the current page;...@>= +@<If the current page is empty and node |p| is to be deleted, |goto done1|; +otherwise use node |p| to update the state of the current page; if this node +is an insertion, |goto contribute|; otherwise if this node is not a legal +breakpoint, |goto contribute| or |update_heights|; otherwise set |pi| to the +penalty associated with this breakpoint@>; +@<Check if node |p| is a new champion breakpoint; then \(if)if it is time +for a page break, prepare for output, and either fire up the user's output +routine and |return| or ship out the page and |goto done|@>; +if ((type(p) < glue_node)||(type(p) > kern_node)) goto contribute; +update_heights: @<Update the current page measurements with respect to the +glue or kern specified by node~|p|@>; +contribute: @<Make sure that |page_max_depth| is not exceeded@>; +@<Link node |p| into the current page and |goto done|@>; +done1: @<Recycle node |p|@>; +done: + +@ @<Link node |p| into the current page and |goto done|@>= +link(page_tail)=p;page_tail=p; +link(contrib_head)=link(p);link(p)=null;goto done + +@ @<Recycle node |p|@>= +link(contrib_head)=link(p);link(p)=null; +if (saving_vdiscards > 0) + {@+if (page_disc==null) page_disc=p;@+else link(tail_page_disc)=p; + tail_page_disc=p; + } +else flush_node_list(p) + +@ The title of this section is already so long, it seems best to avoid +making it more accurate but still longer, by mentioning the fact that a +kern node at the end of the contribution list will not be contributed until +we know its successor. + +@<If the current page is empty...@>= +switch (type(p)) { +case hlist_node: case vlist_node: case rule_node: if (page_contents < box_there) + @<Initialize the current page, insert the \.{\\topskip} glue ahead of +|p|, and |goto resume|@>@; + else@<Prepare to move a box or rule node to the current page, then |goto +contribute|@>@;@+break; +case whatsit_node: @<Prepare to move whatsit |p| to the current page, then +|goto contribute|@>; +case glue_node: if (page_contents < box_there) goto done1; + else if (precedes_break(page_tail)) pi=0; + else goto update_heights;@+break; +case kern_node: if (page_contents < box_there) goto done1; + else if (link(p)==null) return; + else if (type(link(p))==glue_node) pi=0; + else goto update_heights;@+break; +case penalty_node: if (page_contents < box_there) goto done1;@+else pi=penalty(p);@+break; +case mark_node: goto contribute; +case ins_node: @<Append an insertion to the current page and |goto contribute|@>@; +default:confusion("page"); +@:this can't happen page}{\quad page@> +} + +@ @<Initialize the current page, insert the \.{\\topskip} glue...@>= +{@+if (page_contents==empty) freeze_page_specs(box_there); +else page_contents=box_there; +q=new_skip_param(top_skip_code); /*now |temp_ptr==glue_ptr(q)|*/ +if (width(temp_ptr) > height(p)) width(temp_ptr)=width(temp_ptr)-height(p); +else width(temp_ptr)=0; +link(q)=p;link(contrib_head)=q;goto resume; +} + +@ @<Prepare to move a box or rule node to the current page...@>= +{@+page_total=page_total+page_depth+height(p); +page_depth=depth(p); +goto contribute; +} + +@ @<Make sure that |page_max_depth| is not exceeded@>= +if (page_depth > page_max_depth) + {@+page_total=@| + page_total+page_depth-page_max_depth;@/ + page_depth=page_max_depth; + } + +@ @<Update the current page measurements with respect to the glue...@>= +if (type(p)==kern_node) q=p; +else{@+q=glue_ptr(p); + page_so_far[2+stretch_order(q)]=@| + page_so_far[2+stretch_order(q)]+stretch(q);@/ + page_shrink=page_shrink+shrink(q); + if ((shrink_order(q)!=normal)&&(shrink(q)!=0)) + {@+@t@>@;@/ + print_err("Infinite glue shrinkage found on current page");@/ +@.Infinite glue shrinkage...@> + help4("The page about to be output contains some infinitely",@/ + "shrinkable glue, e.g., `\\vss' or `\\vskip 0pt minus 1fil'.",@/ + "Such glue doesn't belong there; but you can safely proceed,",@/ + "since the offensive shrinkability has been made finite."); + error(); + r=new_spec(q);shrink_order(r)=normal;delete_glue_ref(q); + glue_ptr(p)=r;q=r; + } + } +page_total=page_total+page_depth+width(q);page_depth=0 + +@ @<Check if node |p| is a new champion breakpoint; then \(if)...@>= +if (pi < inf_penalty) + {@+@<Compute the badness, |b|, of the current page, using |awful_bad| if +the box is too full@>; + if (b < awful_bad) + if (pi <= eject_penalty) c=pi; + else if (b < inf_bad) c=b+pi+insert_penalties; + else c=deplorable; + else c=b; + if (insert_penalties >= 10000) c=awful_bad; +#ifdef @!STAT + if (tracing_pages > 0) @<Display the page break cost@>; +#endif +@;@/ + if (c <= least_page_cost) + {@+best_page_break=p;best_size=page_goal; + least_page_cost=c; + r=link(page_ins_head); + while (r!=page_ins_head) + {@+best_ins_ptr(r)=last_ins_ptr(r); + r=link(r); + } + } + if ((c==awful_bad)||(pi <= eject_penalty)) + {@+fire_up(p); /*output the current page at the best place*/ + if (output_active) + { @<restore the previous current file, line, and command@>@; + return; /*user's output routine will act*/ + } + goto done; /*the page has been shipped out by default output routine*/ + } + } + +@ @<Display the page break cost@>= +{@+begin_diagnostic();print_nl("%"); +print(" t=");print_totals();@/ +print(" g=");print_scaled(page_goal);@/ +print(" b="); +if (b==awful_bad) print_char('*');@+else print_int(b); +@.*\relax@> +print(" p=");print_int(pi); +print(" c="); +if (c==awful_bad) print_char('*');@+else print_int(c); +if (c <= least_page_cost) print_char('#'); +end_diagnostic(false); +} + +@ @<Compute the badness, |b|, of the current page...@>= +if (page_total < page_goal) + if ((page_so_far[3]!=0)||(page_so_far[4]!=0)||@| + (page_so_far[5]!=0)) b=0; + else b=badness(page_goal-page_total, page_so_far[2]); +else if (page_total-page_goal > page_shrink) b=awful_bad; +else b=badness(page_total-page_goal, page_shrink) + +@ @<Append an insertion to the current page and |goto contribute|@>= +{@+if (page_contents==empty) freeze_page_specs(inserts_only); +n=subtype(p);r=page_ins_head; +while (n >= subtype(link(r))) r=link(r); +n=qo(n); +if (subtype(r)!=qi(n)) + @<Create a page insertion node with |subtype(r)=qi(n)|, and include the +glue correction for box |n| in the current page state@>; +if (type(r)==split_up) insert_penalties=insert_penalties+float_cost(p); +else{@+last_ins_ptr(r)=p; + delta=page_goal-page_total-page_depth+page_shrink; + /*this much room is left if we shrink the maximum*/ + if (count(n)==1000) h=height(p); + else h=x_over_n(height(p), 1000)*count(n); /*this much room is needed*/ + if (((h <= 0)||(h <= delta))&&(height(p)+height(r) <= dimen(n))) + {@+page_goal=page_goal-h;height(r)=height(r)+height(p); + } + else@<Find the best way to split the insertion, and change |type(r)| to +|split_up|@>; + } +goto contribute; +} + +@ We take note of the value of \.{\\skip} |n| and the height plus depth +of \.{\\box}~|n| only when the first \.{\\insert}~|n| node is +encountered for a new page. A user who changes the contents of \.{\\box}~|n| +after that first \.{\\insert}~|n| had better be either extremely careful +or extremely lucky, or both. + +@<Create a page insertion node...@>= +{@+q=get_node(page_ins_node_size);link(q)=link(r);link(r)=q;r=q; +subtype(r)=qi(n);type(r)=inserting;ensure_vbox(n); +if (box(n)==null) height(r)=0; +else height(r)=height(box(n))+depth(box(n)); +best_ins_ptr(r)=null;@/ +q=skip(n); +if (count(n)==1000) h=height(r); +else h=x_over_n(height(r), 1000)*count(n); +page_goal=page_goal-h-width(q);@/ +page_so_far[2+stretch_order(q)]=@|page_so_far[2+stretch_order(q)]+stretch(q);@/ +page_shrink=page_shrink+shrink(q); +if ((shrink_order(q)!=normal)&&(shrink(q)!=0)) + {@+print_err("Infinite glue shrinkage inserted from ");print_esc("skip"); +@.Infinite glue shrinkage...@> + print_int(n); + help3("The correction glue for page breaking with insertions",@/ + "must have finite shrinkability. But you may proceed,",@/ + "since the offensive shrinkability has been made finite."); + error(); + } +} + +@ Here is the code that will split a long footnote between pages, in an +emergency. The current situation deserves to be recapitulated: Node |p| +is an insertion into box |n|; the insertion will not fit, in its entirety, +either because it would make the total contents of box |n| greater than +\.{\\dimen} |n|, or because it would make the incremental amount of growth +|h| greater than the available space |delta|, or both. (This amount |h| has +been weighted by the insertion scaling factor, i.e., by \.{\\count} |n| +over 1000.) Now we will choose the best way to break the vlist of the +insertion, using the same criteria as in the \.{\\vsplit} operation. + +@<Find the best way to split the insertion...@>= +{@+if (count(n) <= 0) w=max_dimen; +else{@+w=page_goal-page_total-page_depth; + if (count(n)!=1000) w=x_over_n(w, count(n))*1000; + } +if (w > dimen(n)-height(r)) w=dimen(n)-height(r); +q=vert_break(ins_ptr(p), w, depth(p)); +height(r)=height(r)+best_height_plus_depth; +#ifdef @!STAT +if (tracing_pages > 0) @<Display the insertion split cost@>; +#endif +@;@/ +if (count(n)!=1000) + best_height_plus_depth=x_over_n(best_height_plus_depth, 1000)*count(n); +page_goal=page_goal-best_height_plus_depth; +type(r)=split_up;broken_ptr(r)=q;broken_ins(r)=p; +if (q==null) insert_penalties=insert_penalties+eject_penalty; +else if (type(q)==penalty_node) insert_penalties=insert_penalties+penalty(q); +} + +@ @<Display the insertion split cost@>= +{@+begin_diagnostic();print_nl("% split");print_int(n); +@.split@> +print(" to ");print_scaled(w); +print_char(',');print_scaled(best_height_plus_depth);@/ +print(" p="); +if (q==null) print_int(eject_penalty); +else if (type(q)==penalty_node) print_int(penalty(q)); +else print_char('0'); +end_diagnostic(false); +} + +@ When the page builder has looked at as much material as could appear before +the next page break, it makes its decision. The break that gave minimum +badness will be used to put a completed ``page'' into box 255, with insertions +appended to their other boxes. + +We also set the values of |top_mark|, |first_mark|, and |bot_mark|. The +program uses the fact that |bot_mark!=null| implies |first_mark!=null|; +it also knows that |bot_mark==null| implies |top_mark==first_mark==null|. + +The |fire_up| subroutine prepares to output the current page at the best +place; then it fires up the user's output routine, if there is one, +or it simply ships out the page. There is one parameter, |c|, which represents +the node that was being contributed to the page when the decision to +force an output was made. + +@<Declare the procedure called |fire_up|@>= +static void fire_up(pointer @!c) +{@+ +pointer p, @!q, @!r, @!s; /*nodes being examined and/or changed*/ +pointer @!prev_p; /*predecessor of |p|*/ +int @!n; /*insertion box number*/ +bool @!wait; /*should the present insertion be held over?*/ +int @!save_vbadness; /*saved value of |vbadness|*/ +scaled @!save_vfuzz; /*saved value of |vfuzz|*/ +pointer @!save_split_top_skip; /*saved value of |split_top_skip|*/ +@<Set the value of |output_penalty|@>; +if (sa_mark!=null) + if (do_marks(fire_up_init, 0, sa_mark)) sa_mark=null; +if (bot_mark!=null) + {@+if (top_mark!=null) delete_token_ref(top_mark); + top_mark=bot_mark;add_token_ref(top_mark); + delete_token_ref(first_mark);first_mark=null; + } +@<Put the \(o)optimal current page into box 255, update |first_mark| and |bot_mark|, +append insertions to their boxes, and put the remaining nodes back on the +contribution list@>; +if (sa_mark!=null) + if (do_marks(fire_up_done, 0, sa_mark)) sa_mark=null; +if ((top_mark!=null)&&(first_mark==null)) + {@+first_mark=top_mark;add_token_ref(top_mark); + } +if (output_routine!=null) + if (dead_cycles >= max_dead_cycles) + @<Explain that too many dead cycles have occurred in a row@>@; + else@<Fire up the user's output routine and |return|@>; +@<Perform the default output routine@>; +} + +@ @<Set the value of |output_penalty|@>= +if (type(best_page_break)==penalty_node) + {@+geq_word_define(int_base+output_penalty_code, penalty(best_page_break)); + penalty(best_page_break)=inf_penalty; + } +else geq_word_define(int_base+output_penalty_code, inf_penalty) + +@ As the page is finally being prepared for output, +pointer |p| runs through the vlist, with |prev_p| trailing behind; +pointer |q| is the tail of a list of insertions that +are being held over for a subsequent page. + +@<Put the \(o)optimal current page into box 255...@>= +if (c==best_page_break) best_page_break=null; /*|c| not yet linked in*/ +@<Ensure that box 255 is empty before output@>; +insert_penalties=0; /*this will count the number of insertions held over*/ +save_split_top_skip=split_top_skip; +if (holding_inserts <= 0) + @<Prepare all the boxes involved in insertions to act as queues@>; +q=hold_head;link(q)=null;prev_p=page_head;p=link(prev_p); +while (p!=best_page_break) + {@+if (type(p)==ins_node) + {@+if (holding_inserts <= 0) + @<Either insert the material specified by node |p| into the appropriate +box, or hold it for the next page; also delete node |p| from the current page@>; + } + else if (type(p)==mark_node) + if (mark_class(p)!=0) @<Update the current marks for |fire_up|@>@; + else@<Update the values of |first_mark| and |bot_mark|@>; + prev_p=p;p=link(prev_p); + } +split_top_skip=save_split_top_skip; +@<Break the current page at node |p|, put it in box~255, and put the remaining +nodes on the contribution list@>; +@<Delete \(t)the page-insertion nodes@>@; + +@ @<Ensure that box 255 is empty before output@>= +if (box(255)!=null) + {@+print_err("");print_esc("box");print("255 is not void"); +@:box255}{\.{\\box255 is not void}@> + help2("You shouldn't use \\box255 except in \\output routines.",@/ + "Proceed, and I'll discard its present contents."); + box_error(255); + } + +@ @<Update the values of |first_mark| and |bot_mark|@>= +{@+if (first_mark==null) + {@+first_mark=mark_ptr(p); + add_token_ref(first_mark); + } +if (bot_mark!=null) delete_token_ref(bot_mark); +bot_mark=mark_ptr(p);add_token_ref(bot_mark); +} + +@ When the following code is executed, the current page runs from node +|link(page_head)| to node |prev_p|, and the nodes from |p| to |page_tail| +are to be placed back at the front of the contribution list. Furthermore +the heldover insertions appear in a list from |link(hold_head)| to |q|; we +will put them into the current page list for safekeeping while the user's +output routine is active. We might have |q==hold_head|; and |p==null| if +and only if |prev_p==page_tail|. Error messages are suppressed within +|vpackage|, since the box might appear to be overfull or underfull simply +because the stretch and shrink from the \.{\\skip} registers for inserts +are not actually present in the box. + +@<Break the current page at node |p|, put it...@>= +if (p!=null) + {@+if (link(contrib_head)==null) + if (nest_ptr==0) tail=page_tail; + else contrib_tail=page_tail; + link(page_tail)=link(contrib_head); + link(contrib_head)=p; + link(prev_p)=null; + } +save_vbadness=vbadness;vbadness=inf_bad; +save_vfuzz=vfuzz;vfuzz=max_dimen; /*inhibit error messages*/ +box(255)=vpackage(link(page_head), best_size, exactly, page_max_depth); +vbadness=save_vbadness;vfuzz=save_vfuzz; +if (last_glue!=max_halfword) delete_glue_ref(last_glue); +@<Start a new current page@>; /*this sets |last_glue=max_halfword|*/ +if (q!=hold_head) + {@+link(page_head)=link(hold_head);page_tail=q; + } + +@ If many insertions are supposed to go into the same box, we want to know +the position of the last node in that box, so that we don't need to waste time +when linking further information into it. The |last_ins_ptr| fields of the +page insertion nodes are therefore used for this purpose during the +packaging phase. + +@<Prepare all the boxes involved in insertions to act as queues@>= +{@+r=link(page_ins_head); +while (r!=page_ins_head) + {@+if (best_ins_ptr(r)!=null) + {@+n=qo(subtype(r));ensure_vbox(n); + if (box(n)==null) box(n)=new_null_box(); + p=box(n)+list_offset; + while (link(p)!=null) p=link(p); + last_ins_ptr(r)=p; + } + r=link(r); + } +} + +@ @<Delete \(t)the page-insertion nodes@>= +r=link(page_ins_head); +while (r!=page_ins_head) + {@+q=link(r);free_node(r, page_ins_node_size);r=q; + } +link(page_ins_head)=page_ins_head + +@ We will set |best_ins_ptr=null| and package the box corresponding to +insertion node~|r|, just after making the final insertion into that box. +If this final insertion is `|split_up|', the remainder after splitting +and pruning (if any) will be carried over to the next page. + +@<Either insert the material specified by node |p| into...@>= +{@+r=link(page_ins_head); +while (subtype(r)!=subtype(p)) r=link(r); +if (best_ins_ptr(r)==null) wait=true; +else{@+wait=false;s=last_ins_ptr(r);link(s)=ins_ptr(p); + if (best_ins_ptr(r)==p) + @<Wrap up the box specified by node |r|, splitting node |p| if called +for; set |wait:=true| if node |p| holds a remainder after splitting@>@; + else{@+while (link(s)!=null) s=link(s); + last_ins_ptr(r)=s; + } + } +@<Either append the insertion node |p| after node |q|, and remove it from +the current page, or delete |node(p)|@>; +} + +@ @<Wrap up the box specified by node |r|, splitting node |p| if...@>= +{@+if (type(r)==split_up) + if ((broken_ins(r)==p)&&(broken_ptr(r)!=null)) + {@+while (link(s)!=broken_ptr(r)) s=link(s); + link(s)=null; + split_top_skip=split_top_ptr(p); + ins_ptr(p)=prune_page_top(broken_ptr(r), false); + if (ins_ptr(p)!=null) + {@+temp_ptr=vpack(ins_ptr(p), natural); + height(p)=height(temp_ptr)+depth(temp_ptr); + free_node(temp_ptr, box_node_size);wait=true; + } + } +best_ins_ptr(r)=null; +n=qo(subtype(r)); +temp_ptr=list_ptr(box(n)); +free_node(box(n), box_node_size); +box(n)=vpack(temp_ptr, natural); +} + +@ @<Either append the insertion node |p|...@>= +link(prev_p)=link(p);link(p)=null; +if (wait) + {@+link(q)=p;q=p;incr(insert_penalties); + } +else{@+delete_glue_ref(split_top_ptr(p)); + free_node(p, ins_node_size); + } +p=prev_p + +@ The list of heldover insertions, running from |link(page_head)| to +|page_tail|, must be moved to the contribution list when the user has +specified no output routine. + +@<Perform the default output routine@>= +{@+if (link(page_head)!=null) + {@+if (link(contrib_head)==null) + if (nest_ptr==0) tail=page_tail;@+else contrib_tail=page_tail; + else link(page_tail)=link(contrib_head); + link(contrib_head)=link(page_head); + link(page_head)=null;page_tail=page_head; + } +flush_node_list(page_disc);page_disc=null; +ship_out(box(255));box(255)=null; +} + +@ @<Explain that too many dead cycles have occurred in a row@>= +{@+print_err("Output loop---");print_int(dead_cycles); +@.Output loop...@> +print(" consecutive dead cycles"); +help3("I've concluded that your \\output is awry; it never does a",@/ +"\\shipout, so I'm shipping \\box255 out myself. Next time",@/ +"increase \\maxdeadcycles if you want me to be more patient!");error(); +} + +@ @<Fire up the user's output routine and |return|@>= +{@+output_active=true; +incr(dead_cycles); +push_nest();mode=-vmode;prev_depth=ignore_depth;mode_line=-line; +begin_token_list(output_routine, output_text); +new_save_level(output_group);normal_paragraph(); +scan_left_brace(); +return; +} + +@ When the user's output routine finishes, it has constructed a vlist +in internal vertical mode, and \TeX\ will do the following: + +@<Resume the page builder after an output routine has come to an end@>= +{@+if ((loc!=null)|| + ((token_type!=output_text)&&(token_type!=backed_up))) + @<Recover from an unbalanced output routine@>; +end_token_list(); /*conserve stack space in case more outputs are triggered*/ +end_graf();unsave();output_active=false;insert_penalties=0;@/ +@<Ensure that box 255 is empty after output@>; +if (tail!=head) /*current list goes after heldover insertions*/ + {@+link(page_tail)=link(head); + page_tail=tail; + } +if (link(page_head)!=null) /*and both go before heldover contributions*/ + {@+if (link(contrib_head)==null) contrib_tail=page_tail; + link(page_tail)=link(contrib_head); + link(contrib_head)=link(page_head); + link(page_head)=null;page_tail=page_head; + } +flush_node_list(page_disc);page_disc=null; +pop_nest();build_page(); +} + +@ @<Recover from an unbalanced output routine@>= +{@+print_err("Unbalanced output routine"); +@.Unbalanced output routine@> +help2("Your sneaky output routine has problematic {'s and/or }'s.",@/ +"I can't handle that very well; good luck.");error(); +@/do@+{get_token(); +}@+ while (!(loc==null)); +} /*loops forever if reading from a file, since |null==min_halfword <= 0|*/ + +@ @<Ensure that box 255 is empty after output@>= +if (box(255)!=null) + {@+print_err("Output routine didn't use all of "); + print_esc("box");print_int(255); +@.Output routine didn't use...@> + help3("Your \\output commands should empty \\box255,",@/ + "e.g., by saying `\\shipout\\box255'.",@/ + "Proceed; I'll discard its present contents."); + box_error(255); + } + +@* The chief executive. +We come now to the |main_control| routine, which contains the master +switch that causes all the various pieces of \TeX\ to do their things, +in the right order. + +In a sense, this is the grand climax of the program: It applies all the +tools that we have worked so hard to construct. In another sense, this is +the messiest part of the program: It necessarily refers to other pieces +of code all over the place, so that a person can't fully understand what is +going on without paging back and forth to be reminded of conventions that +are defined elsewhere. We are now at the hub of the web, the central nervous +system that touches most of the other parts and ties them together. +@^brain@> + +The structure of |main_control| itself is quite simple. There's a label +called |big_switch|, at which point the next token of input is fetched +using |get_x_token|. Then the program branches at high speed into one of +about 100 possible directions, based on the value of the current +mode and the newly fetched command code; the sum |abs(mode)+cur_cmd| +indicates what to do next. For example, the case `|vmode+letter|' arises +when a letter occurs in vertical mode (or internal vertical mode); this +case leads to instructions that initialize a new paragraph and enter +horizontal mode. + +The big |case| statement that contains this multiway switch has been labeled +|reswitch|, so that the program can |goto reswitch| when the next token +has already been fetched. Most of the cases are quite short; they call +an ``action procedure'' that does the work for that case, and then they +either |goto reswitch| or they ``fall through'' to the end of the |case| +statement, which returns control back to |big_switch|. Thus, |main_control| +is not an extremely large procedure, in spite of the multiplicity of things +it must do; it is small enough to be handled by \PASCAL\ compilers that put +severe restrictions on procedure size. +@!@^action procedure@> + +One case is singled out for special treatment, because it accounts for most +of \TeX's activities in typical applications. The process of reading simple +text and converting it into |char_node| records, while looking for ligatures +and kerns, is part of \TeX's ``inner loop''; the whole program runs +efficiently when its inner loop is fast, so this part has been written +with particular care. + +@ We shall concentrate first on the inner loop of |main_control|, deferring +consideration of the other cases until later. +@^inner loop@> + +@p @t\4@>@<Declare action procedures for use by |main_control|@>@; +@t\4@>@<Declare the procedure called |handle_right_brace|@>@; +static void main_control(void) /*governs \TeX's activities*/ +{@+ +int @!t; /*general-purpose temporary variable*/ +@<Initialize profiling@>@; +if (every_job!=null) begin_token_list(every_job, every_job_text); +big_switch: + @<record timing information@>@; + get_x_token();@/ +big_reswitch: + @<set current file, line, and command for the current time slot@>@; +@<Give diagnostic information, if requested@>; +switch (abs(mode)+cur_cmd) { +case hmode+letter: case hmode+other_char: + case hmode+char_given: goto main_loop; +case hmode+char_num: {@+scan_char_num();cur_chr=cur_val;goto main_loop;@+} +case hmode+no_boundary: {@+get_x_token(); + if ((cur_cmd==letter)||(cur_cmd==other_char)||(cur_cmd==char_given)|| + (cur_cmd==char_num)) cancel_boundary=true; + goto big_reswitch; + } +case hmode+spacer: if (space_factor==1000) goto append_normal_space; + else app_space();@+break; +case hmode+ex_space: case mmode+ex_space: goto append_normal_space; +@t\4@>@<Cases of |main_control| that are not part of the inner loop@>@; +} /*of the big |case| statement*/ +goto big_switch; +main_loop: +@<record timing information@>@; +@<set current file, line, and command for the current time slot@>@; +@<Append character |cur_chr| and the following characters (if~any) +to the current hlist in the current font; |goto big_reswitch| when a non-character +has been fetched@>; +append_normal_space: @<Append a normal inter-word space to the current list, +then |goto big_switch|@>; +} + +@ When a new token has just been fetched at |big_switch|, we have an +ideal place to monitor \TeX's activity. +@^debugging@> + +@<Give diagnostic information, if requested@>= +if (interrupt!=0) if (OK_to_interrupt) + {@+back_input();check_interrupt;goto big_switch; + } +#ifdef @!DEBUG +if (panicking) check_mem(false);@; +#endif +if (tracing_commands > 0) show_cur_cmd_chr() + +@ The following part of the program was first written in a structured +manner, according to the philosophy that ``premature optimization is +the root of all evil.'' Then it was rearranged into pieces of +spaghetti so that the most common actions could proceed with little or +no redundancy. + +The original unoptimized form of this algorithm resembles the +|reconstitute| procedure, which was described earlier in connection with +hyphenation. Again we have an implied ``cursor'' between characters +|cur_l| and |cur_r|. The main difference is that the |lig_stack| can now +contain a charnode as well as pseudo-ligatures; that stack is now +usually nonempty, because the next character of input (if any) has been +appended to it. In |main_control| we have +$$|cur_r|=\cases{|character(lig_stack)|,&if |lig_stack > null|;\cr + |font_bchar[cur_font]|,&otherwise;\cr}$$ +except when |character(lig_stack)==font_false_bchar[cur_font]|. +Several additional global variables are needed. + +@<Glob...@>= +static internal_font_number @!main_f; /*the current font*/ +static four_quarters @!main_i; /*character information bytes for |cur_l|*/ +static four_quarters @!main_j; /*ligature/kern command*/ +static font_index @!main_k; /*index into |font_info|*/ +static pointer @!main_p; /*temporary register for list manipulation*/ +static int @!main_s; /*space factor value*/ +static halfword @!bchar; /*boundary character of current font, or |non_char|*/ +static halfword @!false_bchar; /*nonexistent character matching |bchar|, or |non_char|*/ +static bool @!cancel_boundary; /*should the left boundary be ignored?*/ +static bool @!ins_disc; /*should we insert a discretionary node?*/ + +@ The boolean variables of the main loop are normally false, and always reset +to false before the loop is left. That saves us the extra work of initializing +each time. + +@<Set init...@>= +ligature_present=false;cancel_boundary=false;lft_hit=false;rt_hit=false; +ins_disc=false; + +@ We leave the |space_factor| unchanged if |sf_code(cur_chr)==0|; otherwise we +set it equal to |sf_code(cur_chr)|, except that it should never change +from a value less than 1000 to a value exceeding 1000. The most common +case is |sf_code(cur_chr)==1000|, so we want that case to be fast. + +The overall structure of the main loop is presented here. Some program labels +are inside the individual sections. +@^inner loop@> + +@d adjust_space_factor @t@>@;@/ + main_s=sf_code(cur_chr); + if (main_s==1000) space_factor=1000; + else if (main_s < 1000) + {@+if (main_s > 0) space_factor=main_s; + } + else if (space_factor < 1000) space_factor=1000; + else space_factor=main_s + +@<Append character |cur_chr|...@>= +adjust_space_factor;@/ +main_f=cur_font; +bchar=font_bchar[main_f];false_bchar=font_false_bchar[main_f]; +if (mode > 0) if (language!=clang) fix_language(); +fast_get_avail(lig_stack);font(lig_stack)=main_f;cur_l=qi(cur_chr); +character(lig_stack)=cur_l;@/ +cur_q=tail; +if (cancel_boundary) + {@+cancel_boundary=false;main_k=non_address; + } +else main_k=bchar_label[main_f]; +if (main_k==non_address) goto main_loop_move2; /*no left boundary processing*/ +cur_r=cur_l;cur_l=non_char; +goto main_lig_loop1; /*begin with cursor after left boundary*/ +@# +main_loop_wrapup: @<Make a ligature node, if |ligature_present|; insert a +null discretionary, if appropriate@>; +main_loop_move: @<If the cursor is immediately followed by the right boundary, +|goto big_reswitch|; if it's followed by an invalid character, |goto big_switch|; +otherwise move the cursor one step to the right and |goto main_lig_loop|@>; +main_loop_lookahead: @<Look ahead for another character, or leave |lig_stack| +empty if there's none there@>; +main_lig_loop: @<If there's a ligature/kern command relevant to |cur_l| and +|cur_r|, adjust the text appropriately; exit to |main_loop_wrapup|@>; +main_loop_move_lig: @<Move the cursor past a pseudo-ligature, then |goto main_loop_lookahead| +or |main_lig_loop|@>@; + +@ If |link(cur_q)| is nonnull when |wrapup| is invoked, |cur_q| points to +the list of characters that were consumed while building the ligature +character~|cur_l|. + +A discretionary break is not inserted for an explicit hyphen when we are in +restricted horizontal mode. In particular, this avoids putting discretionary +nodes inside of other discretionaries. +@^inner loop@> + +@d pack_lig(X) /*the parameter is either |rt_hit| or |false|*/ + {@+main_p=new_ligature(main_f, cur_l, link(cur_q)); + if (lft_hit) + {@+subtype(main_p)=2;lft_hit=false; + } + if (X) if (lig_stack==null) + {@+incr(subtype(main_p));rt_hit=false; + } + link(cur_q)=main_p;tail=main_p;ligature_present=false; + } + +@d wrapup(A) if (cur_l < non_char) + {@+if (link(cur_q) > null) + if (character(tail)==qi(hyphen_char[main_f])) ins_disc=true; + if (ligature_present) pack_lig(A); + if (ins_disc) + {@+ins_disc=false; + if (mode > 0) tail_append(new_disc()); + } + } + +@<Make a ligature node, if |ligature_present|;...@>= +wrapup(rt_hit) + +@ @<If the cursor is immediately followed by the right boundary...@>= +@^inner loop@> +if (lig_stack==null) goto big_reswitch; +cur_q=tail;cur_l=character(lig_stack); +main_loop_move1: if (!is_char_node(lig_stack)) goto main_loop_move_lig; +main_loop_move2: if ((cur_chr < font_bc[main_f])||(cur_chr > font_ec[main_f])) + {@+char_warning(main_f, cur_chr);free_avail(lig_stack);goto big_switch; + } +main_i=char_info(main_f, cur_l); +if (!char_exists(main_i)) + {@+char_warning(main_f, cur_chr);free_avail(lig_stack);goto big_switch; + } +link(tail)=lig_stack;tail=lig_stack /*|main_loop_lookahead| is next*/ + +@ Here we are at |main_loop_move_lig|. +When we begin this code we have |cur_q==tail| and |cur_l==character(lig_stack)|. + +@<Move the cursor past a pseudo-ligature...@>= +main_p=lig_ptr(lig_stack); +if (main_p > null) tail_append(main_p); /*append a single character*/ +temp_ptr=lig_stack;lig_stack=link(temp_ptr); +free_node(temp_ptr, small_node_size); +main_i=char_info(main_f, cur_l);ligature_present=true; +if (lig_stack==null) + if (main_p > null) goto main_loop_lookahead; + else cur_r=bchar; +else cur_r=character(lig_stack); +goto main_lig_loop + +@ The result of \.{\\char} can participate in a ligature or kern, so we must +look ahead for it. + +@<Look ahead for another character...@>= +get_next(); /*set only |cur_cmd| and |cur_chr|, for speed*/ +if (cur_cmd==letter) goto main_loop_lookahead1; +if (cur_cmd==other_char) goto main_loop_lookahead1; +if (cur_cmd==char_given) goto main_loop_lookahead1; +x_token(); /*now expand and set |cur_cmd|, |cur_chr|, |cur_tok|*/ +if (cur_cmd==letter) goto main_loop_lookahead1; +if (cur_cmd==other_char) goto main_loop_lookahead1; +if (cur_cmd==char_given) goto main_loop_lookahead1; +if (cur_cmd==char_num) + {@+scan_char_num();cur_chr=cur_val;goto main_loop_lookahead1; + } +if (cur_cmd==no_boundary) bchar=non_char; +cur_r=bchar;lig_stack=null;goto main_lig_loop; +main_loop_lookahead1: adjust_space_factor; +fast_get_avail(lig_stack);font(lig_stack)=main_f; +cur_r=qi(cur_chr);character(lig_stack)=cur_r; +if (cur_r==false_bchar) cur_r=non_char /*this prevents spurious ligatures*/ + +@ Even though comparatively few characters have a lig/kern program, several +of the instructions here count as part of \TeX's inner loop, since a +@^inner loop@> +potentially long sequential search must be performed. For example, tests with +Computer Modern Roman showed that about 40 per cent of all characters +actually encountered in practice had a lig/kern program, and that about four +lig/kern commands were investigated for every such character. + +At the beginning of this code we have |main_i==char_info(main_f, cur_l)|. + +@<If there's a ligature/kern command...@>= +if (char_tag(main_i)!=lig_tag) goto main_loop_wrapup; +if (cur_r==non_char) goto main_loop_wrapup; +main_k=lig_kern_start(main_f, main_i);main_j=font_info[main_k].qqqq; +if (skip_byte(main_j) <= stop_flag) goto main_lig_loop2; +main_k=lig_kern_restart(main_f, main_j); +main_lig_loop1: main_j=font_info[main_k].qqqq; +main_lig_loop2: if (next_char(main_j)==cur_r) + if (skip_byte(main_j) <= stop_flag) + @<Do ligature or kern command, returning to |main_lig_loop| or |main_loop_wrapup| +or |main_loop_move|@>; +if (skip_byte(main_j)==qi(0)) incr(main_k); +else{@+if (skip_byte(main_j) >= stop_flag) goto main_loop_wrapup; + main_k=main_k+qo(skip_byte(main_j))+1; + } +goto main_lig_loop1 + +@ When a ligature or kern instruction matches a character, we know from +|read_font_info| that the character exists in the font, even though we +haven't verified its existence in the normal way. + +This section could be made into a subroutine, if the code inside +|main_control| needs to be shortened. + +\chardef\@@='174 % vertical line to indicate character retention + +@<Do ligature or kern command...@>= +{@+if (op_byte(main_j) >= kern_flag) + {@+wrapup(rt_hit); + tail_append(new_kern(char_kern(main_f, main_j)));goto main_loop_move; + } +if (cur_l==non_char) lft_hit=true; +else if (lig_stack==null) rt_hit=true; +check_interrupt; /*allow a way out in case there's an infinite ligature loop*/ +switch (op_byte(main_j)) { +case qi(1): case qi(5): {@+cur_l=rem_byte(main_j); /*\.{=:\@@}, \.{=:\@@>}*/ + main_i=char_info(main_f, cur_l);ligature_present=true; + } @+break; +case qi(2): case qi(6): {@+cur_r=rem_byte(main_j); /*\.{\@@=:}, \.{\@@=:>}*/ + if (lig_stack==null) /*right boundary character is being consumed*/ + {@+lig_stack=new_lig_item(cur_r);bchar=non_char; + } + else if (is_char_node(lig_stack)) /*|link(lig_stack)==null|*/ + {@+main_p=lig_stack;lig_stack=new_lig_item(cur_r); + lig_ptr(lig_stack)=main_p; + } + else character(lig_stack)=cur_r; + } @+break; +case qi(3): {@+cur_r=rem_byte(main_j); /*\.{\@@=:\@@}*/ + main_p=lig_stack;lig_stack=new_lig_item(cur_r); + link(lig_stack)=main_p; + } @+break; +case qi(7): case qi(11): {@+wrapup(false); /*\.{\@@=:\@@>}, \.{\@@=:\@@>>}*/ + cur_q=tail;cur_l=rem_byte(main_j); + main_i=char_info(main_f, cur_l);ligature_present=true; + } @+break; +default:{@+cur_l=rem_byte(main_j);ligature_present=true; /*\.{=:}*/ + if (lig_stack==null) goto main_loop_wrapup; + else goto main_loop_move1; + } +} +if (op_byte(main_j) > qi(4)) + if (op_byte(main_j)!=qi(7)) goto main_loop_wrapup; +if (cur_l < non_char) goto main_lig_loop; +main_k=bchar_label[main_f];goto main_lig_loop1; +} + +@ The occurrence of blank spaces is almost part of \TeX's inner loop, +@^inner loop@> +since we usually encounter about one space for every five non-blank characters. +Therefore |main_control| gives second-highest priority to ordinary spaces. + +When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will +see to it later that the corresponding glue specification is precisely +|zero_glue|, not merely a pointer to some specification that happens +to be full of zeroes. Therefore it is simple to test whether a glue parameter +is zero or~not. + +@<Append a normal inter-word space...@>= +if (space_skip==zero_glue) + {@+@<Find the glue specification, |main_p|, for text spaces in the current +font@>; + temp_ptr=new_glue(main_p); + } +else temp_ptr=new_param_glue(space_skip_code); +link(tail)=temp_ptr;tail=temp_ptr; +goto big_switch + +@ Having |font_glue| allocated for each text font saves both time and memory. +If any of the three spacing parameters are subsequently changed by the +use of \.{\\fontdimen}, the |find_font_dimen| procedure deallocates the +|font_glue| specification allocated here. + +@<Find the glue specification...@>= +{@+main_p=font_glue[cur_font]; +if (main_p==null) + {@+main_p=new_spec(zero_glue);main_k=param_base[cur_font]+space_code; + width(main_p)=font_info[main_k].sc; /*that's |space(cur_font)|*/ + stretch(main_p)=font_info[main_k+1].sc; /*and |space_stretch(cur_font)|*/ + shrink(main_p)=font_info[main_k+2].sc; /*and |space_shrink(cur_font)|*/ + font_glue[cur_font]=main_p; + } +} + +@ @<Declare act...@>= +static void app_space(void) /*handle spaces when |space_factor!=1000|*/ +{@+pointer @!q; /*glue node*/ +if ((space_factor >= 2000)&&(xspace_skip!=zero_glue)) + q=new_param_glue(xspace_skip_code); +else{@+if (space_skip!=zero_glue) main_p=space_skip; + else@<Find the glue specification...@>; + main_p=new_spec(main_p); + @<Modify the glue specification in |main_p| according to the space factor@>; + q=new_glue(main_p);glue_ref_count(main_p)=null; + } +link(tail)=q;tail=q; +} + +@ @<Modify the glue specification in |main_p| according to the space factor@>= +if (space_factor >= 2000) width(main_p)=width(main_p)+extra_space(cur_font); +stretch(main_p)=xn_over_d(stretch(main_p), space_factor, 1000); +shrink(main_p)=xn_over_d(shrink(main_p), 1000, space_factor) + +@ Whew---that covers the main loop. We can now proceed at a leisurely +pace through the other combinations of possibilities. + +@d any_mode(A) case vmode+A: case hmode+A: + case mmode+A /*for mode-independent commands*/ + +@<Cases of |main_control| that are not part of the inner loop@>= +any_mode(relax): case vmode+spacer: case mmode+spacer: + case mmode+no_boundary: do_nothing;@+break; +any_mode(ignore_spaces): {@+@<Get the next non-blank non-call...@>; + goto big_reswitch; + } +case vmode+stop: + if (its_all_over()) + { @<record the end of \TeX@>@; + return; /*this is the only way out*/ + } + @+break; +@t\4@>@<Forbidden cases detected in |main_control|@>@+@, any_mode(mac_param): + report_illegal_case();@+break; +@<Math-only cases in non-math modes, or vice versa@>: insert_dollar_sign();@+break; +@t\4@>@<Cases of |main_control| that build boxes and lists@>@; +@t\4@>@<Cases of |main_control| that don't depend on |mode|@>@; +@t\4@>@<Cases of |main_control| that are for extensions to \TeX@>@; + +@ Here is a list of cases where the user has probably gotten into or out of math +mode by mistake. \TeX\ will insert a dollar sign and rescan the current token. + +@d non_math(A) case vmode+A: case hmode+A + +@<Math-only cases in non-math modes...@>= +non_math(sup_mark): non_math(sub_mark): non_math(math_char_num): +non_math(math_given): non_math(math_comp): non_math(delim_num): +non_math(left_right): non_math(above): non_math(radical): +non_math(math_style): non_math(math_choice): non_math(vcenter): +non_math(non_script): non_math(mkern): non_math(limit_switch): +non_math(mskip): non_math(math_accent): +case mmode+endv: case mmode+par_end: case mmode+stop: + case mmode+vskip: case mmode+un_vbox: +case mmode+valign: case mmode+hrule + +@ @<Declare action...@>= +static void insert_dollar_sign(void) +{@+back_input();cur_tok=math_shift_token+'$'; +print_err("Missing $ inserted"); +@.Missing \$ inserted@> +help2("I've inserted a begin-math/end-math symbol since I think",@/ +"you left one out. Proceed, with fingers crossed.");ins_error(); +} + +@ When erroneous situations arise, \TeX\ usually issues an error message +specific to the particular error. For example, `\.{\\noalign}' should +not appear in any mode, since it is recognized by the |align_peek| routine +in all of its legitimate appearances; a special error message is given +when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate +error message is simply that the user is not allowed to do what he or she +has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode, +and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated +here and in the other sections referred to under `See also \dots.' + +@<Forbidden cases...@>= +case vmode+vmove: case hmode+hmove: case mmode+hmove: any_mode(last_item): + +@ The `|you_cant|' procedure prints a line saying that the current command +is illegal in the current mode; it identifies these things symbolically. + +@<Declare action...@>= +static void you_cant(void) +{@+print_err("You can't use `"); +@.You can't use x in y mode@> +print_cmd_chr(cur_cmd, cur_chr); +print("' in ");print_mode(mode); +} + +@ @<Declare act...@>= +static void report_illegal_case(void) +{@+you_cant(); +help4("Sorry, but I'm not programmed to handle this case;",@/ +"I'll just pretend that you didn't ask for it.",@/ +"If you're in the wrong mode, you might be able to",@/ +"return to the right one by typing `I}' or `I$' or `I\\par'.");@/ +error(); +} + +@ Some operations are allowed only in privileged modes, i.e., in cases +that |mode > 0|. The |privileged| function is used to detect violations +of this rule; it issues an error message and returns |false| if the +current |mode| is negative. + +@<Declare act...@>= +static bool privileged(void) +{@+if (mode > 0) return true; +else{@+report_illegal_case();return false; + } +} + +@ Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the +endgame, since both of them have `|stop|' as their command code. + +@<Put each...@>= +primitive("end", stop, 0);@/ +@!@:end\_}{\.{\\end} primitive@> +primitive("dump", stop, 1);@/ +@!@:dump\_}{\.{\\dump} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case stop: if (chr_code==1) print_esc("dump");@+else print_esc("end");@+break; + +@ We don't want to leave |main_control| immediately when a |stop| command +is sensed, because it may be necessary to invoke an \.{\\output} routine +several times before things really grind to a halt. (The output routine +might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.) +Therefore |its_all_over| is |true| only when the current page +and contribution list are empty, and when the last output was not a +``dead cycle.'' + +@<Declare act...@>= +static bool its_all_over(void) /*do this when \.{\\end} or \.{\\dump} occurs*/ +{@+ +if (privileged()) + {@+if ((page_head==page_tail)&&(head==tail)&&(dead_cycles==0)) + {@+return true; + } + back_input(); /*we will try to end again after ejecting residual material*/ + tail_append(new_null_box()); + width(tail)=hsize; + tail_append(new_glue(fill_glue)); + tail_append(new_penalty(-010000000000));@/ + build_page(); /*append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000}*/ + } +return false; +} + +@* Building boxes and lists. +The most important parts of |main_control| are concerned with \TeX's +chief mission of box-making. We need to control the activities that put +entries on vlists and hlists, as well as the activities that convert +those lists into boxes. All of the necessary machinery has already been +developed; it remains for us to ``push the buttons'' at the right times. + +@ As an introduction to these routines, let's consider one of the simplest +cases: What happens when `\.{\\hrule}' occurs in vertical mode, or +`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control| +is short, since the |scan_rule_spec| routine already does most of what is +required; thus, there is no need for a special action procedure. + +Note that baselineskip calculations are disabled after a rule in vertical +mode, by setting |prev_depth=ignore_depth|. + +@<Cases of |main_control| that build...@>= +case vmode+hrule: case hmode+vrule: case mmode+vrule: {@+tail_append(scan_rule_spec()); + if (abs(mode)==vmode) prev_depth=ignore_depth; + else if (abs(mode)==hmode) space_factor=1000; + } @+break; + +@ The processing of things like \.{\\hskip} and \.{\\vskip} is slightly +more complicated. But the code in |main_control| is very short, since +it simply calls on the action routine |append_glue|. Similarly, \.{\\kern} +activates |append_kern|. + +@<Cases of |main_control| that build...@>= +case vmode+vskip: case hmode+hskip: case mmode+hskip: + case mmode+mskip: append_glue();@+break; +any_mode(kern): case mmode+mkern: append_kern();@+break; + +@ The |hskip| and |vskip| command codes are used for control sequences +like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. +The difference is in the value of |cur_chr|. + +@d fil_code 0 /*identifies \.{\\hfil} and \.{\\vfil}*/ +@d fill_code 1 /*identifies \.{\\hfill} and \.{\\vfill}*/ +@d ss_code 2 /*identifies \.{\\hss} and \.{\\vss}*/ +@d fil_neg_code 3 /*identifies \.{\\hfilneg} and \.{\\vfilneg}*/ +@d skip_code 4 /*identifies \.{\\hskip} and \.{\\vskip}*/ +@d mskip_code 5 /*identifies \.{\\mskip}*/ + +@<Put each...@>= +primitive("hskip", hskip, skip_code);@/ +@!@:hskip\_}{\.{\\hskip} primitive@> +primitive("hfil", hskip, fil_code); +@!@:hfil\_}{\.{\\hfil} primitive@> +primitive("hfill", hskip, fill_code);@/ +@!@:hfill\_}{\.{\\hfill} primitive@> +primitive("hss", hskip, ss_code); +@!@:hss\_}{\.{\\hss} primitive@> +primitive("hfilneg", hskip, fil_neg_code);@/ +@!@:hfil\_neg\_}{\.{\\hfilneg} primitive@> +primitive("vskip", vskip, skip_code);@/ +@!@:vskip\_}{\.{\\vskip} primitive@> +primitive("vfil", vskip, fil_code); +@!@:vfil\_}{\.{\\vfil} primitive@> +primitive("vfill", vskip, fill_code);@/ +@!@:vfill\_}{\.{\\vfill} primitive@> +primitive("vss", vskip, ss_code); +@!@:vss\_}{\.{\\vss} primitive@> +primitive("vfilneg", vskip, fil_neg_code);@/ +@!@:vfil\_neg\_}{\.{\\vfilneg} primitive@> +primitive("mskip", mskip, mskip_code);@/ +@!@:mskip\_}{\.{\\mskip} primitive@> +primitive("kern", kern, explicit); +@!@:kern\_}{\.{\\kern} primitive@> +primitive("mkern", mkern, mu_glue);@/ +@!@:mkern\_}{\.{\\mkern} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case hskip: switch (chr_code) { + case skip_code: print_esc("hskip");@+break; + case fil_code: print_esc("hfil");@+break; + case fill_code: print_esc("hfill");@+break; + case ss_code: print_esc("hss");@+break; + default:print_esc("hfilneg"); + } @+break; +case vskip: switch (chr_code) { + case skip_code: print_esc("vskip");@+break; + case fil_code: print_esc("vfil");@+break; + case fill_code: print_esc("vfill");@+break; + case ss_code: print_esc("vss");@+break; + default:print_esc("vfilneg"); + } @+break; +case mskip: print_esc("mskip");@+break; +case kern: print_esc("kern");@+break; +case mkern: print_esc("mkern");@+break; + +@ All the work relating to glue creation has been relegated to the +following subroutine. It does not call |build_page|, because it is +used in at least one place where that would be a mistake. + +@<Declare action...@>= +static void append_glue(void) +{@+small_number s; /*modifier of skip command*/ +s=cur_chr; +switch (s) { +case fil_code: cur_val=fil_glue;@+break; +case fill_code: cur_val=fill_glue;@+break; +case ss_code: cur_val=ss_glue;@+break; +case fil_neg_code: cur_val=fil_neg_glue;@+break; +case skip_code: scan_glue(glue_val);@+break; +case mskip_code: scan_glue(mu_val); +} /*now |cur_val| points to the glue specification*/ +tail_append(new_glue(cur_val)); +if (s >= skip_code) + {@+decr(glue_ref_count(cur_val)); + if (s > skip_code) subtype(tail)=mu_glue; + } +} + +@ @<Declare act...@>= +static void append_kern(void) +{@+quarterword s; /*|subtype| of the kern node*/ +s=cur_chr;scan_dimen(s==mu_glue, false, false); +tail_append(new_kern(cur_val));subtype(tail)=s; +} + +@ Many of the actions related to box-making are triggered by the appearance +of braces in the input. For example, when the user says `\.{\\hbox} +\.{to} \.{100pt\{$\langle\,\hbox{\rm hlist}\,\rangle$\}}' in vertical mode, +the information about the box size (100pt, |exactly|) is put onto |save_stack| +with a level boundary word just above it, and |cur_group=adjusted_hbox_group|; +\TeX\ enters restricted horizontal mode to process the hlist. The right +brace eventually causes |save_stack| to be restored to its former state, +at which time the information about the box size (100pt, |exactly|) is +available once again; a box is packaged and we leave restricted horizontal +mode, appending the new box to the current list of the enclosing mode +(in this case to the current list of vertical mode), followed by any +vertical adjustments that were removed from the box by |hpack|. + +The next few sections of the program are therefore concerned with the +treatment of left and right curly braces. + +@ If a left brace occurs in the middle of a page or paragraph, it simply +introduces a new level of grouping, and the matching right brace will not have +such a drastic effect. Such grouping affects neither the mode nor the +current list. + +@<Cases of |main_control| that build...@>= +non_math(left_brace): new_save_level(simple_group);@+break; +any_mode(begin_group): new_save_level(semi_simple_group);@+break; +any_mode(end_group): if (cur_group==semi_simple_group) unsave(); + else off_save();@+break; + +@ We have to deal with errors in which braces and such things are not +properly nested. Sometimes the user makes an error of commission by +inserting an extra symbol, but sometimes the user makes an error of omission. +\TeX\ can't always tell one from the other, so it makes a guess and tries +to avoid getting into a loop. + +The |off_save| routine is called when the current group code is wrong. It tries +to insert something into the user's input that will help clean off +the top level. + +@<Declare act...@>= +static void off_save(void) +{@+pointer p; /*inserted token*/ +if (cur_group==bottom_level) + @<Drop current token and complain that it was unmatched@>@; +else{@+back_input();p=get_avail();link(temp_head)=p; + print_err("Missing "); + @<Prepare to insert a token that matches |cur_group|, and print what it +is@>; + print(" inserted");ins_list(link(temp_head)); + help5("I've inserted something that you may have forgotten.",@/ + "(See the <inserted text> above.)",@/ + "With luck, this will get me unwedged. But if you",@/ + "really didn't forget anything, try typing `2' now; then",@/ + "my insertion and my current dilemma will both disappear."); + error(); + } +} + +@ At this point, |link(temp_head)==p|, a pointer to an empty one-word node. + +@<Prepare to insert a token that matches |cur_group|...@>= +switch (cur_group) { +case semi_simple_group: {@+info(p)=cs_token_flag+frozen_end_group; + print_esc("endgroup"); +@.Missing \\endgroup inserted@> + } @+break; +case math_shift_group: {@+info(p)=math_shift_token+'$';print_char('$'); +@.Missing \$ inserted@> + } @+break; +case math_left_group: {@+info(p)=cs_token_flag+frozen_right;link(p)=get_avail(); + p=link(p);info(p)=other_token+'.';print_esc("right."); +@.Missing \\right\hbox{.} inserted@> +@^null delimiter@> + } @+break; +default:{@+info(p)=right_brace_token+'}';print_char('}'); +@.Missing \} inserted@> + } +} + +@ @<Drop current token and complain that it was unmatched@>= +{@+print_err("Extra ");print_cmd_chr(cur_cmd, cur_chr); +@.Extra x@> +help1("Things are pretty mixed up, but I think the worst is over.");@/ +error(); +} + +@ The routine for a |right_brace| character branches into many subcases, +since a variety of things may happen, depending on |cur_group|. Some +types of groups are not supposed to be ended by a right brace; error +messages are given in hopes of pinpointing the problem. Most branches +of this routine will be filled in later, when we are ready to understand +them; meanwhile, we must prepare ourselves to deal with such errors. + +@<Cases of |main_control| that build...@>= +any_mode(right_brace): handle_right_brace();@+break; + +@ @<Declare the procedure called |handle_right_brace|@>= +static void handle_right_brace(void) +{@+pointer p, @!q; /*for short-term use*/ +scaled @!d; /*holds |split_max_depth| in |insert_group|*/ +int @!f; /*holds |floating_penalty| in |insert_group|*/ +switch (cur_group) { +case simple_group: unsave();@+break; +case bottom_level: {@+print_err("Too many }'s"); +@.Too many \}'s@> + help2("You've closed more groups than you opened.",@/ + "Such booboos are generally harmless, so keep going.");error(); + } @+break; +case semi_simple_group: case math_shift_group: + case math_left_group: extra_right_brace();@+break; +@t\4@>@<Cases of |handle_right_brace| where a |right_brace| triggers a delayed +action@>@; +default:confusion("rightbrace"); +@:this can't happen rightbrace}{\quad rightbrace@> +} +} + +@ @<Declare act...@>= +static void extra_right_brace(void) +{@+print_err("Extra }, or forgotten "); +@.Extra \}, or forgotten x@> +switch (cur_group) { +case semi_simple_group: print_esc("endgroup");@+break; +case math_shift_group: print_char('$');@+break; +case math_left_group: print_esc("right"); +} @/ +help5("I've deleted a group-closing symbol because it seems to be",@/ +"spurious, as in `$x}$'. But perhaps the } is legitimate and",@/ +"you forgot something else, as in `\\hbox{$x}'. In such cases",@/ +"the way to recover is to insert both the forgotten and the",@/ +"deleted material, e.g., by typing `I$}'.");error(); +incr(align_state); +} + +@ Here is where we clear the parameters that are supposed to revert to their +default values after every paragraph and when internal vertical mode is entered. + +@<Declare act...@>= +static void normal_paragraph(void) +{@+if (looseness!=0) eq_word_define(int_base+looseness_code, 0); +if (hang_indent!=0) eq_word_define(dimen_base+hang_indent_code, 0); +if (hang_after!=1) eq_word_define(int_base+hang_after_code, 1); +if (par_shape_ptr!=null) eq_define(par_shape_loc, shape_ref, null); +if (inter_line_penalties_ptr!=null) + eq_define(inter_line_penalties_loc, shape_ref, null); +} + +@ Now let's turn to the question of how \.{\\hbox} is treated. We actually +need to consider also a slightly larger context, since constructions like +`\.{\\setbox3=}\penalty0\.{\\hbox...}' and +`\.{\\leaders}\penalty0\.{\\hbox...}' and +`\.{\\lower3.8pt\\hbox...}' +are supposed to invoke quite +different actions after the box has been packaged. Conversely, +constructions like `\.{\\setbox3=}' can be followed by a variety of +different kinds of boxes, and we would like to encode such things in an +efficient way. + +In other words, there are two problems: to represent the context of a box, +and to represent its type. + +The first problem is solved by putting a ``context code'' on the |save_stack|, +just below the two entries that give the dimensions produced by |scan_spec|. +The context code is either a (signed) shift amount, or it is a large +integer | >= box_flag|, where |box_flag==@t$2^{30}$@>|. Codes |box_flag| through +|global_box_flag-1| represent `\.{\\setbox0}' through `\.{\\setbox32767}'; +codes |global_box_flag| through |ship_out_flag-1| represent +`\.{\\global\\setbox0}' through `\.{\\global\\setbox32767}'; +code |ship_out_flag| represents `\.{\\shipout}'; and codes |leader_flag| +through |leader_flag+2| represent `\.{\\leaders}', `\.{\\cleaders}', +and `\.{\\xleaders}'. + +The second problem is solved by giving the command code |make_box| to all +control sequences that produce a box, and by using the following |chr_code| +values to distinguish between them: |box_code|, |copy_code|, |last_box_code|, +|vsplit_code|, |vtop_code|, |vtop_code+vmode|, and |vtop_code+hmode|, where +the latter two are used to denote \.{\\vbox} and \.{\\hbox}, respectively. + +@d box_flag 010000000000 /*context code for `\.{\\setbox0}'*/ +@d global_box_flag 010000100000 /*context code for `\.{\\global\\setbox0}'*/ +@d ship_out_flag 010000200000 /*context code for `\.{\\shipout}'*/ +@d leader_flag 010000200001 /*context code for `\.{\\leaders}'*/ +@d box_code 0 /*|chr_code| for `\.{\\box}'*/ +@d copy_code 1 /*|chr_code| for `\.{\\copy}'*/ +@d last_box_code 2 /*|chr_code| for `\.{\\lastbox}'*/ +@d vsplit_code 3 /*|chr_code| for `\.{\\vsplit}'*/ +@d vtop_code 4 /*|chr_code| for `\.{\\vtop}'*/ + +@<Put each...@>= +primitive("moveleft", hmove, 1); +@!@:move\_left\_}{\.{\\moveleft} primitive@> +primitive("moveright", hmove, 0);@/ +@!@:move\_right\_}{\.{\\moveright} primitive@> +primitive("raise", vmove, 1); +@!@:raise\_}{\.{\\raise} primitive@> +primitive("lower", vmove, 0); +@!@:lower\_}{\.{\\lower} primitive@> +@# +primitive("box", make_box, box_code); +@!@:box\_}{\.{\\box} primitive@> +primitive("copy", make_box, copy_code); +@!@:copy\_}{\.{\\copy} primitive@> +primitive("lastbox", make_box, last_box_code); +@!@:last\_box\_}{\.{\\lastbox} primitive@> +primitive("vsplit", make_box, vsplit_code); +@!@:vsplit\_}{\.{\\vsplit} primitive@> +primitive("vtop", make_box, vtop_code);@/ +@!@:vtop\_}{\.{\\vtop} primitive@> +primitive("vbox", make_box, vtop_code+vmode); +@!@:vbox\_}{\.{\\vbox} primitive@> +primitive("hbox", make_box, vtop_code+hmode);@/ +@!@:hbox\_}{\.{\\hbox} primitive@> +primitive("shipout", leader_ship, a_leaders-1); /*|ship_out_flag==leader_flag-1|*/ +@!@:ship\_out\_}{\.{\\shipout} primitive@> +primitive("leaders", leader_ship, a_leaders); +@!@:leaders\_}{\.{\\leaders} primitive@> +primitive("cleaders", leader_ship, c_leaders); +@!@:c\_leaders\_}{\.{\\cleaders} primitive@> +primitive("xleaders", leader_ship, x_leaders); +@!@:x\_leaders\_}{\.{\\xleaders} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case hmove: if (chr_code==1) print_esc("moveleft");@+else print_esc("moveright");@+break; +case vmove: if (chr_code==1) print_esc("raise");@+else print_esc("lower");@+break; +case make_box: switch (chr_code) { + case box_code: print_esc("box");@+break; + case copy_code: print_esc("copy");@+break; + case last_box_code: print_esc("lastbox");@+break; + case vsplit_code: print_esc("vsplit");@+break; + case vtop_code: print_esc("vtop");@+break; + case vtop_code+vmode: print_esc("vbox");@+break; + default:print_esc("hbox"); + } @+break; +case leader_ship: if (chr_code==a_leaders) print_esc("leaders"); + else if (chr_code==c_leaders) print_esc("cleaders"); + else if (chr_code==x_leaders) print_esc("xleaders"); + else print_esc("shipout");@+break; + +@ Constructions that require a box are started by calling |scan_box| with +a specified context code. The |scan_box| routine verifies +that a |make_box| command comes next and then it calls |begin_box|. + +@<Cases of |main_control| that build...@>= +case vmode+hmove: case hmode+vmove: case mmode+vmove: {@+t=cur_chr; + scan_normal_dimen; + if (t==0) scan_box(cur_val);@+else scan_box(-cur_val); + } @+break; +any_mode(leader_ship): scan_box(leader_flag-a_leaders+cur_chr);@+break; +any_mode(make_box): begin_box(0);@+break; + +@ The global variable |cur_box| will point to a newly made box. If the box +is void, we will have |cur_box==null|. Otherwise we will have +|type(cur_box)==hlist_node| or |vlist_node| or |rule_node|; the |rule_node| +case can occur only with leaders. + +@<Glob...@>= +static pointer @!cur_box; /*box to be placed into its context*/ + +@ The |box_end| procedure does the right thing with |cur_box|, if +|box_context| represents the context as explained above. + +@<Declare act...@>= +static void box_end(int @!box_context) +{@+pointer p; /*|ord_noad| for new box in math mode*/ +small_number @!a; /*global prefix*/ +if (box_context < box_flag) @<Append box |cur_box| to the current list, shifted +by |box_context|@>@; +else if (box_context < ship_out_flag) @<Store \(c)|cur_box| in a box register@>@; +else if (cur_box!=null) + if (box_context > ship_out_flag) @<Append a new leader node that uses |cur_box|@>@; + else ship_out(cur_box); +} + +@ The global variable |adjust_tail| will be non-null if and only if the +current box might include adjustments that should be appended to the +current vertical list. + +@<Append box |cur_box| to the current...@>= +{@+if (cur_box!=null) + {@+shift_amount(cur_box)=box_context; + if (abs(mode)==vmode) + {@+append_to_vlist(cur_box); + if (adjust_tail!=null) + {@+if (adjust_head!=adjust_tail) + {@+link(tail)=link(adjust_head);tail=adjust_tail; + } + adjust_tail=null; + } + if (mode > 0) build_page(); + } + else{@+if (abs(mode)==hmode) space_factor=1000; + else{@+p=new_noad(); + math_type(nucleus(p))=sub_box; + info(nucleus(p))=cur_box;cur_box=p; + } + link(tail)=cur_box;tail=cur_box; + } + } +} + +@ @<Store \(c)|cur_box| in a box register@>= +{@+if (box_context < global_box_flag) + {@+cur_val=box_context-box_flag;a=0; + } +else{@+cur_val=box_context-global_box_flag;a=4; + } +if (cur_val < 256) define(box_base+cur_val, box_ref, cur_box); +else sa_def_box; +} + +@ @<Append a new leader node...@>= +{@+@<Get the next non-blank non-relax...@>; +if (((cur_cmd==hskip)&&(abs(mode)!=vmode))||@| + ((cur_cmd==vskip)&&(abs(mode)==vmode))) + {@+append_glue();subtype(tail)=box_context-(leader_flag-a_leaders); + leader_ptr(tail)=cur_box; + } +else{@+print_err("Leaders not followed by proper glue"); +@.Leaders not followed by...@> + help3("You should say `\\leaders <box or rule><hskip or vskip>'.",@/ + "I found the <box or rule>, but there's no suitable",@/ + "<hskip or vskip>, so I'm ignoring these leaders.");back_error(); + flush_node_list(cur_box); + } +} + +@ Now that we can see what eventually happens to boxes, we can consider +the first steps in their creation. The |begin_box| routine is called when +|box_context| is a context specification, |cur_chr| specifies the type of +box desired, and |cur_cmd==make_box|. + +@<Declare act...@>= +static void begin_box(int @!box_context) +{@+ +pointer @!p, @!q; /*run through the current list*/ +int @!m; /*the length of a replacement list*/ +halfword @!k; /*0 or |vmode| or |hmode|*/ +halfword @!n; /*a box number*/ +switch (cur_chr) { +case box_code: {@+scan_register_num();fetch_box(cur_box); + change_box(null); /*the box becomes void, at the same level*/ + } @+break; +case copy_code: {@+scan_register_num();fetch_box(q);cur_box=copy_node_list(q); + } @+break; +case last_box_code: @<If the current list ends with a box node, delete it +from the list and make |cur_box| point to it; otherwise set |cur_box:=null|@>@;@+break; +case vsplit_code: @<Split off part of a vertical box, make |cur_box| point +to it@>@;@+break; +default:@<Initiate the construction of an hbox or vbox, then |return|@>@; +} @/ +box_end(box_context); /*in simple cases, we use the box immediately*/ +} + +@ Note that the condition |!is_char_node(tail)| implies that |head!=tail|, +since |head| is a one-word node. + +@<If the current list ends with a box node, delete it...@>= +{@+cur_box=null; +if (abs(mode)==mmode) + {@+you_cant();help1("Sorry; this \\lastbox will be void.");error(); + } +else if ((mode==vmode)&&(head==tail)) + {@+you_cant(); + help2("Sorry...I usually can't take things from the current page.",@/ + "This \\lastbox will therefore be void.");error(); + } +else{@+if (!is_char_node(tail)) + if ((type(tail)==hlist_node)||(type(tail)==vlist_node)) + @<Remove the last box, unless it's part of a discretionary@>; + } +} + +@ @<Remove the last box...@>= +{@+q=head; +@/do@+{p=q; +if (!is_char_node(q)) if (type(q)==disc_node) + {@+for (m=1; m<=replace_count(q); m++) p=link(p); + if (p==tail) goto done; + } +q=link(p); +}@+ while (!(q==tail)); +cur_box=tail;shift_amount(cur_box)=0; +tail=p;link(p)=null; +done: ;} + +@ Here we deal with things like `\.{\\vsplit 13 to 100pt}'. + +@<Split off part of a vertical box, make |cur_box| point to it@>= +{@+scan_register_num();n=cur_val; +if (!scan_keyword("to")) +@.to@> + {@+print_err("Missing `to' inserted"); +@.Missing `to' inserted@> + help2("I'm working on `\\vsplit<box number> to <dimen>';",@/ + "will look for the <dimen> next.");error(); + } +scan_normal_dimen; +cur_box=vsplit(n, cur_val); +} + +@ Here is where we enter restricted horizontal mode or internal vertical +mode, in order to make a box. + +@<Initiate the construction of an hbox or vbox, then |return|@>= +{@+k=cur_chr-vtop_code;saved(0)=box_context; +if (k==hmode) + if ((box_context < box_flag)&&(abs(mode)==vmode)) + scan_spec(adjusted_hbox_group, true); + else scan_spec(hbox_group, true); +else{@+if (k==vmode) scan_spec(vbox_group, true); + else{@+scan_spec(vtop_group, true);k=vmode; + } + normal_paragraph(); + } +push_nest();mode=-k; +if (k==vmode) + {@+prev_depth=ignore_depth; + if (every_vbox!=null) begin_token_list(every_vbox, every_vbox_text); + } +else{@+space_factor=1000; + if (every_hbox!=null) begin_token_list(every_hbox, every_hbox_text); + } +return; +} + +@ @<Declare act...@>= +static void scan_box(int @!box_context) + /*the next input should specify a box or perhaps a rule*/ +{@+@<Get the next non-blank non-relax...@>; +if (cur_cmd==make_box) begin_box(box_context); +else if ((box_context >= leader_flag)&&((cur_cmd==hrule)||(cur_cmd==vrule))) + {@+cur_box=scan_rule_spec();box_end(box_context); + } +else{@+@t@>@;@/ + print_err("A <box> was supposed to be here");@/ +@.A <box> was supposed to...@> + help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or",@/ + "something like that. So you might find something missing in",@/ + "your output. But keep trying; you can fix this later.");back_error(); + } +} + +@ When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or +\.{\\vtop} construction, the |package| routine comes into action. We might +also have to finish a paragraph that hasn't ended. + +@<Cases of |handle...@>= +case hbox_group: package(0);@+break; +case adjusted_hbox_group: {@+adjust_tail=adjust_head;package(0); + } @+break; +case vbox_group: {@+end_graf();package(0); + } @+break; +case vtop_group: {@+end_graf();package(vtop_code); + } @+break; + +@ @<Declare action...@>= +static void package(small_number @!c) +{@+scaled h; /*height of box*/ +pointer @!p; /*first node in a box*/ +scaled @!d; /*max depth*/ +d=box_max_depth;unsave();save_ptr=save_ptr-3; +if (mode==-hmode) cur_box=hpack(link(head), saved(2), saved(1)); +else{@+cur_box=vpackage(link(head), saved(2), saved(1), d); + if (c==vtop_code) @<Readjust the height and depth of |cur_box|, for \.{\\vtop}@>; + } +pop_nest();box_end(saved(0)); +} + +@ The height of a `\.{\\vtop}' box is inherited from the first item on its list, +if that item is an |hlist_node|, |vlist_node|, or |rule_node|; otherwise +the \.{\\vtop} height is zero. + + +@<Readjust the height...@>= +{@+h=0;p=list_ptr(cur_box); +if (p!=null) if (type(p) <= rule_node) h=height(p); +depth(cur_box)=depth(cur_box)-h+height(cur_box);height(cur_box)=h; +} + +@ A paragraph begins when horizontal-mode material occurs in vertical mode, +or when the paragraph is explicitly started by `\.{\\indent}' or +`\.{\\noindent}'. + +@<Put each...@>= +primitive("indent", start_par, 1); +@!@:indent\_}{\.{\\indent} primitive@> +primitive("noindent", start_par, 0); +@!@:no\_indent\_}{\.{\\noindent} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case start_par: if (chr_code==0) print_esc("noindent");@+else print_esc("indent");@+break; + +@ @<Cases of |main_control| that build...@>= +case vmode+start_par: new_graf(cur_chr > 0);@+break; +case vmode+letter: case vmode+other_char: + case vmode+char_num: case vmode+char_given: + case vmode+math_shift: case vmode+un_hbox: + case vmode+vrule: + case vmode+accent: case vmode+discretionary: + case vmode+hskip: case vmode+valign: + case vmode+ex_space: case vmode+no_boundary: @t@>@;@/ + {@+back_input();new_graf(true); + } @+break; + +@ @<Declare act...@>= +static small_number norm_min(int @!h) +{@+if (h <= 0) return 1;@+else if (h >= 63) return 63;@+ +else return h; +} +@# +static void new_graf(bool @!indented) +{@+prev_graf=0; +if ((mode==vmode)||(head!=tail)) + tail_append(new_param_glue(par_skip_code)); +push_nest();mode=hmode;space_factor=1000;set_cur_lang;clang=cur_lang; +prev_graf=(norm_min(left_hyphen_min)*0100+norm_min(right_hyphen_min)) + *0200000+cur_lang; +if (indented) + {@+tail=new_null_box();link(head)=tail;width(tail)=par_indent;@+ + } +if (every_par!=null) begin_token_list(every_par, every_par_text); +if (nest_ptr==1) build_page(); /*put |par_skip| glue on current page*/ +} + +@ @<Cases of |main_control| that build...@>= +case hmode+start_par: case mmode+start_par: indent_in_hmode();@+break; + +@ @<Declare act...@>= +static void indent_in_hmode(void) +{@+pointer p, @!q; +if (cur_chr > 0) /*\.{\\indent}*/ + {@+p=new_null_box();width(p)=par_indent; + if (abs(mode)==hmode) space_factor=1000; + else{@+q=new_noad();math_type(nucleus(q))=sub_box; + info(nucleus(q))=p;p=q; + } + tail_append(p); + } +} + +@ A paragraph ends when a |par_end| command is sensed, or when we are in +horizontal mode when reaching the right brace of vertical-mode routines +like \.{\\vbox}, \.{\\insert}, or \.{\\output}. + +@<Cases of |main_control| that build...@>= +case vmode+par_end: {@+normal_paragraph(); + if (mode > 0) build_page(); + } @+break; +case hmode+par_end: {@+if (align_state < 0) off_save(); /*this tries to + recover from an alignment that didn't end properly*/ + end_graf(); /*this takes us to the enclosing mode, if |mode > 0|*/ + if (mode==vmode) build_page(); + } @+break; +case hmode+stop: case hmode+vskip: case hmode+hrule: + case hmode+un_vbox: case hmode+halign: head_for_vmode();@+break; + +@ @<Declare act...@>= +static void head_for_vmode(void) +{@+if (mode < 0) + if (cur_cmd!=hrule) off_save(); + else{@+print_err("You can't use `"); + print_esc("hrule");print("' here except with leaders"); +@.You can't use \\hrule...@> + help2("To put a horizontal rule in an hbox or an alignment,",@/ + "you should use \\leaders or \\hrulefill (see The TeXbook)."); + error(); + } +else{@+back_input();cur_tok=par_token;back_input();token_type=inserted; + } +} + +@ @<Declare act...@>= +static void end_graf(void) +{@+if (mode==hmode) + {@+if (head==tail) pop_nest(); /*null paragraphs are ignored*/ + else line_break(widow_penalty); + normal_paragraph(); + error_count=0; + } +} + +@ Insertion and adjustment and mark nodes are constructed by the following +pieces of the program. + +@<Cases of |main_control| that build...@>= +any_mode(insert): case hmode+vadjust: + case mmode+vadjust: begin_insert_or_adjust();@+break; +any_mode(mark): make_mark();@+break; + +@ @<Forbidden...@>= +case vmode+vadjust: + +@ @<Declare act...@>= +static void begin_insert_or_adjust(void) +{@+if (cur_cmd==vadjust) cur_val=255; +else{@+scan_eight_bit_int(); + if (cur_val==255) + {@+print_err("You can't ");print_esc("insert");print_int(255); +@.You can't \\insert255@> + help1("I'm changing to \\insert0; box 255 is special."); + error();cur_val=0; + } + } +saved(0)=cur_val;incr(save_ptr); +new_save_level(insert_group);scan_left_brace();normal_paragraph(); +push_nest();mode=-vmode;prev_depth=ignore_depth; +} + +@ @<Cases of |handle...@>= +case insert_group: {@+end_graf();q=split_top_skip;add_glue_ref(q); + d=split_max_depth;f=floating_penalty;unsave();decr(save_ptr); + /*now |saved(0)| is the insertion number, or 255 for |vadjust|*/ + p=vpack(link(head), natural);pop_nest(); + if (saved(0) < 255) + {@+tail_append(get_node(ins_node_size)); + type(tail)=ins_node;subtype(tail)=qi(saved(0)); + height(tail)=height(p)+depth(p);ins_ptr(tail)=list_ptr(p); + split_top_ptr(tail)=q;depth(tail)=d;float_cost(tail)=f; + } + else{@+tail_append(get_node(small_node_size)); + type(tail)=adjust_node;@/ + subtype(tail)=0; /*the |subtype| is not used*/ + adjust_ptr(tail)=list_ptr(p);delete_glue_ref(q); + } + free_node(p, box_node_size); + if (nest_ptr==0) build_page(); + } @+break; +case output_group: @<Resume the page builder...@>@;@+break; + +@ @<Declare act...@>= +static void make_mark(void) +{@+pointer p; /*new node*/ +halfword @!c; /*the mark class*/ +if (cur_chr==0) c=0; +else{@+scan_register_num();c=cur_val; + } +p=scan_toks(false, true);p=get_node(small_node_size); +mark_class(p)=c; +type(p)=mark_node;subtype(p)=0; /*the |subtype| is not used*/ +mark_ptr(p)=def_ref;link(tail)=p;tail=p; +} + +@ Penalty nodes get into a list via the |break_penalty| command. +@^penalties@> + +@<Cases of |main_control| that build...@>= +any_mode(break_penalty): append_penalty();@+break; + +@ @<Declare action...@>= +static void append_penalty(void) +{@+scan_int();tail_append(new_penalty(cur_val)); +if (mode==vmode) build_page(); +} + +@ The |remove_item| command removes a penalty, kern, or glue node if it +appears at the tail of the current list, using a brute-force linear scan. +Like \.{\\lastbox}, this command is not allowed in vertical mode (except +internal vertical mode), since the current list in vertical mode is sent +to the page builder. But if we happen to be able to implement it in +vertical mode, we do. + +@<Cases of |main_control| that build...@>= +any_mode(remove_item): delete_last();@+break; + +@ When |delete_last| is called, |cur_chr| is the |type| of node that +will be deleted, if present. + +@<Declare action...@>= +static void delete_last(void) +{@+ +pointer @!p, @!q; /*run through the current list*/ +int @!m; /*the length of a replacement list*/ +if ((mode==vmode)&&(tail==head)) + @<Apologize for inability to do the operation now, unless \.{\\unskip} follows +non-glue@>@; +else{@+if (!is_char_node(tail)) if (type(tail)==cur_chr) + {@+q=head; + @/do@+{p=q; + if (!is_char_node(q)) if (type(q)==disc_node) + {@+for (m=1; m<=replace_count(q); m++) p=link(p); + if (p==tail) return; + } + q=link(p); + }@+ while (!(q==tail)); + link(p)=null;flush_node_list(tail);tail=p; + } + } +} + +@ @<Apologize for inability to do the operation...@>= +{@+if ((cur_chr!=glue_node)||(last_glue!=max_halfword)) + {@+you_cant(); + help2("Sorry...I usually can't take things from the current page.",@/ + "Try `I\\vskip-\\lastskip' instead."); + if (cur_chr==kern_node) help_line[0]= + ("Try `I\\kern-\\lastkern' instead."); + else if (cur_chr!=glue_node) help_line[0]=@| + ("Perhaps you can make the output routine do it."); + error(); + } +} + +@ @<Put each...@>= +primitive("unpenalty", remove_item, penalty_node);@/ +@!@:un\_penalty\_}{\.{\\unpenalty} primitive@> +primitive("unkern", remove_item, kern_node);@/ +@!@:un\_kern\_}{\.{\\unkern} primitive@> +primitive("unskip", remove_item, glue_node);@/ +@!@:un\_skip\_}{\.{\\unskip} primitive@> +primitive("unhbox", un_hbox, box_code);@/ +@!@:un\_hbox\_}{\.{\\unhbox} primitive@> +primitive("unhcopy", un_hbox, copy_code);@/ +@!@:un\_hcopy\_}{\.{\\unhcopy} primitive@> +primitive("unvbox", un_vbox, box_code);@/ +@!@:un\_vbox\_}{\.{\\unvbox} primitive@> +primitive("unvcopy", un_vbox, copy_code);@/ +@!@:un\_vcopy\_}{\.{\\unvcopy} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case remove_item: if (chr_code==glue_node) print_esc("unskip"); + else if (chr_code==kern_node) print_esc("unkern"); + else print_esc("unpenalty");@+break; +case un_hbox: if (chr_code==copy_code) print_esc("unhcopy"); + else print_esc("unhbox");@+break; +case un_vbox: if (chr_code==copy_code) print_esc("unvcopy") + @<Cases of |un_vbox| for |print_cmd_chr|@>;@/ + else print_esc("unvbox");@+break; + +@ The |un_hbox| and |un_vbox| commands unwrap one of the 256 current boxes. + +@<Cases of |main_control| that build...@>= +case vmode+un_vbox: case hmode+un_hbox: + case mmode+un_hbox: unpackage();@+break; + +@ @<Declare act...@>= +static void unpackage(void) +{@+ +pointer p; /*the box*/ +int @!c; /*should we copy?*/ +if (cur_chr > copy_code) @<Handle saved items and |goto done|@>; +c=cur_chr;scan_register_num();fetch_box(p); +if (p==null) return; +if ((abs(mode)==mmode)||((abs(mode)==vmode)&&(type(p)!=vlist_node))||@| + ((abs(mode)==hmode)&&(type(p)!=hlist_node))) + {@+print_err("Incompatible list can't be unboxed"); +@.Incompatible list...@> + help3("Sorry, Pandora. (You sneaky devil.)",@/ + "I refuse to unbox an \\hbox in vertical mode or vice versa.",@/ + "And I can't open any boxes in math mode.");@/ + error();return; + } +if (c==copy_code) link(tail)=copy_node_list(list_ptr(p)); +else{@+link(tail)=list_ptr(p);change_box(null); + free_node(p, box_node_size); + } +done: +while (link(tail)!=null) tail=link(tail); +} + +@ @<Forbidden...@>=case vmode+ital_corr: + +@ Italic corrections are converted to kern nodes when the |ital_corr| command +follows a character. In math mode the same effect is achieved by appending +a kern of zero here, since italic corrections are supplied later. + +@<Cases of |main_control| that build...@>= +case hmode+ital_corr: append_italic_correction();@+break; +case mmode+ital_corr: tail_append(new_kern(0))@;@+break; + +@ @<Declare act...@>= +static void append_italic_correction(void) +{@+ +pointer p; /*|char_node| at the tail of the current list*/ +internal_font_number @!f; /*the font in the |char_node|*/ +if (tail!=head) + {@+if (is_char_node(tail)) p=tail; + else if (type(tail)==ligature_node) p=lig_char(tail); + else return; + f=font(p); + tail_append(new_kern(char_italic(f, char_info(f, character(p))))); + subtype(tail)=explicit; + } +} + +@ Discretionary nodes are easy in the common case `\.{\\-}', but in the +general case we must process three braces full of items. + +@<Put each...@>= +primitive("-", discretionary, 1); +@!@:Single-character primitives -}{\quad\.{\\-}@> +primitive("discretionary", discretionary, 0); +@!@:discretionary\_}{\.{\\discretionary} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case discretionary: if (chr_code==1) + print_esc("-");@+else print_esc("discretionary");@+break; + +@ @<Cases of |main_control| that build...@>= +case hmode+discretionary: case mmode+discretionary: append_discretionary();@+break; + +@ The space factor does not change when we append a discretionary node, +but it starts out as 1000 in the subsidiary lists. + +@<Declare act...@>= +static void append_discretionary(void) +{@+int c; /*hyphen character*/ +tail_append(new_disc()); +if (cur_chr==1) + {@+c=hyphen_char[cur_font]; + if (c >= 0) if (c < 256) pre_break(tail)=new_character(cur_font, c); + } +else{@+incr(save_ptr);saved(-1)=0;new_save_level(disc_group); + scan_left_brace();push_nest();mode=-hmode;space_factor=1000; + } +} + +@ The three discretionary lists are constructed somewhat as if they were +hboxes. A~subroutine called |build_discretionary| handles the transitions. +(This is sort of fun.) + +@<Cases of |handle...@>= +case disc_group: build_discretionary();@+break; + +@ @<Declare act...@>= +static void build_discretionary(void) +{@+ +pointer p, @!q; /*for link manipulation*/ +int @!n; /*length of discretionary list*/ +unsave(); +@<Prune the current list, if necessary, until it contains only |char_node|, +|kern_node|, |hlist_node|, |vlist_node|, |rule_node|, and |ligature_node| +items; set |n| to the length of the list, and set |q| to the list's tail@>; +p=link(head);pop_nest(); +switch (saved(-1)) { +case 0: pre_break(tail)=p;@+break; +case 1: post_break(tail)=p;@+break; +case 2: @<Attach list |p| to the current list, and record its length; then +finish up and |return|@>; +} /*there are no other cases*/ +incr(saved(-1));new_save_level(disc_group);scan_left_brace(); +push_nest();mode=-hmode;space_factor=1000; +} + +@ @<Attach list |p| to the current...@>= +{@+if ((n > 0)&&(abs(mode)==mmode)) + {@+print_err("Illegal math ");print_esc("discretionary"); +@.Illegal math \\disc...@> + help2("Sorry: The third part of a discretionary break must be",@/ + "empty, in math formulas. I had to delete your third part."); + flush_node_list(p);n=0;error(); + } +else link(tail)=p; +if (n <= max_quarterword) replace_count(tail)=n; +else{@+print_err("Discretionary list is too long"); +@.Discretionary list is too long@> + help2("Wow---I never thought anybody would tweak me here.",@/ + "You can't seriously need such a huge discretionary list?"); + error(); + } +if (n > 0) tail=q; +decr(save_ptr);return; +} + +@ During this loop, |p==link(q)| and there are |n| items preceding |p|. + +@<Prune the current list, if necessary...@>= +q=head;p=link(q);n=0; +while (p!=null) + {@+if (!is_char_node(p)) if (type(p) > rule_node) + if (type(p)!=kern_node) if (type(p)!=ligature_node) + {@+print_err("Improper discretionary list"); +@.Improper discretionary list@> + help1("Discretionary lists must contain only boxes and kerns.");@/ + error(); + begin_diagnostic(); + print_nl("The following discretionary sublist has been deleted:"); +@.The following...deleted@> + show_box(p); + end_diagnostic(true); + flush_node_list(p);link(q)=null;goto done; + } + q=p;p=link(q);incr(n); + } +done: + +@ We need only one more thing to complete the horizontal mode routines, namely +the \.{\\accent} primitive. + +@<Cases of |main_control| that build...@>= +case hmode+accent: make_accent();@+break; + +@ The positioning of accents is straightforward but tedious. Given an accent +of width |a|, designed for characters of height |x| and slant |s|; +and given a character of width |w|, height |h|, and slant |t|: We will shift +the accent down by |x-h|, and we will insert kern nodes that have the effect of +centering the accent over the character and shifting the accent to the +right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is +absent from the font, we will simply use the other, without shifting. + +@<Declare act...@>= +static void make_accent(void) +{@+double s, @!t; /*amount of slant*/ +pointer @!p, @!q, @!r; /*character, box, and kern nodes*/ +internal_font_number @!f; /*relevant font*/ +scaled @!a, @!h, @!x, @!w, @!delta; /*heights and widths, as explained above*/ +four_quarters @!i; /*character information*/ +scan_char_num();f=cur_font;p=new_character(f, cur_val); +if (p!=null) + {@+x=x_height(f);s=slant(f)/float_constant(65536); +@^real division@> + a=char_width(f, char_info(f, character(p)));@/ + do_assignments();@/ + @<Create a character node |q| for the next character, but set |q:=null| +if problems arise@>; + if (q!=null) @<Append the accent with appropriate kerns, then set |p:=q|@>; + link(tail)=p;tail=p;space_factor=1000; + } +} + +@ @<Create a character node |q| for the next...@>= +q=null;f=cur_font; +if ((cur_cmd==letter)||(cur_cmd==other_char)||(cur_cmd==char_given)) + q=new_character(f, cur_chr); +else if (cur_cmd==char_num) + {@+scan_char_num();q=new_character(f, cur_val); + } +else back_input() + +@ The kern nodes appended here must be distinguished from other kerns, lest +they be wiped away by the hyphenation algorithm or by a previous line break. + +The two kerns are computed with (machine-dependent) |double| arithmetic, but +their sum is machine-independent; the net effect is machine-independent, +because the user cannot remove these nodes nor access them via \.{\\lastkern}. + +@<Append the accent with appropriate kerns...@>= +{@+t=slant(f)/float_constant(65536); +@^real division@> +i=char_info(f, character(q)); +w=char_width(f, i);h=char_height(f, height_depth(i)); +if (h!=x) /*the accent must be shifted up or down*/ + {@+p=hpack(p, natural);shift_amount(p)=x-h; + } +delta=round((w-a)/float_constant(2)+h*t-x*s); +@^real multiplication@> +@^real addition@> +r=new_kern(delta);subtype(r)=acc_kern;link(tail)=r;link(r)=p; +tail=new_kern(-a-delta);subtype(tail)=acc_kern;link(p)=tail;p=q; +} + +@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner +into |main_control|, it might be that the user has foolishly inserted +one of them into something that has nothing to do with alignment. But it is +far more likely that a left brace or right brace has been omitted, since +|get_next| takes actions appropriate to alignment only when `\.{\\cr}' +or `\.{\\span}' or tab marks occur with |align_state==0|. The following +program attempts to make an appropriate recovery. + +@<Cases of |main_control| that build...@>= +any_mode(car_ret): any_mode(tab_mark): align_error();@+break; +any_mode(no_align): no_align_error();@+break; +any_mode(omit): omit_error();@+break; + +@ @<Declare act...@>= +static void align_error(void) +{@+if (abs(align_state) > 2) + @<Express consternation over the fact that no alignment is in progress@>@; +else{@+back_input(); + if (align_state < 0) + {@+print_err("Missing { inserted"); +@.Missing \{ inserted@> + incr(align_state);cur_tok=left_brace_token+'{'; + } + else{@+print_err("Missing } inserted"); +@.Missing \} inserted@> + decr(align_state);cur_tok=right_brace_token+'}'; + } + help3("I've put in what seems to be necessary to fix",@/ + "the current column of the current alignment.",@/ + "Try to go on, since this might almost work.");ins_error(); + } +} + +@ @<Express consternation...@>= +{@+print_err("Misplaced ");print_cmd_chr(cur_cmd, cur_chr); +@.Misplaced \&@> +@.Misplaced \\span@> +@.Misplaced \\cr@> +if (cur_tok==tab_token+'&') + {@+help6("I can't figure out why you would want to use a tab mark",@/ + "here. If you just want an ampersand, the remedy is",@/ + "simple: Just type `I\\&' now. But if some right brace",@/ + "up above has ended a previous alignment prematurely,",@/ + "you're probably due for more error messages, and you",@/ + "might try typing `S' now just to see what is salvageable."); + } +else{@+help5("I can't figure out why you would want to use a tab mark",@/ + "or \\cr or \\span just now. If something like a right brace",@/ + "up above has ended a previous alignment prematurely,",@/ + "you're probably due for more error messages, and you",@/ + "might try typing `S' now just to see what is salvageable."); + } +error(); +} + +@ The help messages here contain a little white lie, since \.{\\noalign} +and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. + +@<Declare act...@>= +static void no_align_error(void) +{@+print_err("Misplaced ");print_esc("noalign"); +@.Misplaced \\noalign@> +help2("I expect to see \\noalign only after the \\cr of",@/ + "an alignment. Proceed, and I'll ignore this case.");error(); +} +static void omit_error(void) +{@+print_err("Misplaced ");print_esc("omit"); +@.Misplaced \\omit@> +help2("I expect to see \\omit only after tab marks or the \\cr of",@/ + "an alignment. Proceed, and I'll ignore this case.");error(); +} + +@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. +Let's take a look at what happens when they are used correctly. + +@<Cases of |main_control| that build...@>= +case vmode+halign: case hmode+valign: init_align();@+break; +case mmode+halign: if (privileged()) + if (cur_group==math_shift_group) init_align(); + else off_save();@+break; +case vmode+endv: case hmode+endv: do_endv();@+break; + +@ An |align_group| code is supposed to remain on the |save_stack| +during an entire alignment, until |fin_align| removes it. + +A devious user might force an |endv| command to occur just about anywhere; +we must defeat such hacks. + +@<Declare act...@>= +static void do_endv(void) +{@+base_ptr=input_ptr;input_stack[base_ptr]=cur_input; +while ((input_stack[base_ptr].index_field!=v_template)&& + (input_stack[base_ptr].loc_field==null)&& + (input_stack[base_ptr].state_field==token_list)) decr(base_ptr); +if ((input_stack[base_ptr].index_field!=v_template)|| + (input_stack[base_ptr].loc_field!=null)|| + (input_stack[base_ptr].state_field!=token_list)) + fatal_error("(interwoven alignment preambles are not allowed)"); +@.interwoven alignment preambles...@> + if (cur_group==align_group) + {@+end_graf(); + if (fin_col()) fin_row(); + } +else off_save(); +} + +@ @<Cases of |handle_right_brace|...@>= +case align_group: {@+back_input();cur_tok=cs_token_flag+frozen_cr; + print_err("Missing ");print_esc("cr");print(" inserted"); +@.Missing \\cr inserted@> + help1("I'm guessing that you meant to end an alignment here."); + ins_error(); + } @+break; + +@ @<Cases of |handle_right_brace|...@>= +case no_align_group: {@+end_graf();unsave();align_peek(); + } @+break; + +@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. + +@<Cases of |main_control| that build...@>= +any_mode(end_cs_name): cs_error();@+break; + +@ @<Declare act...@>= +static void cs_error(void) +{@+print_err("Extra ");print_esc("endcsname"); +@.Extra \\endcsname@> +help1("I'm ignoring this, since I wasn't doing a \\csname."); +error(); +} + +@* Building math lists. +The routines that \TeX\ uses to create mlists are similar to those we have +just seen for the generation of hlists and vlists. But it is necessary to +make ``noads'' as well as nodes, so the reader should review the +discussion of math mode data structures before trying to make sense out of +the following program. + +Here is a little routine that needs to be done whenever a subformula +is about to be processed. The parameter is a code like |math_group|. + +@<Declare act...@>= +static void push_math(group_code @!c) +{@+push_nest();mode=-mmode;incompleat_noad=null;new_save_level(c); +} + +@ We get into math mode from horizontal mode when a `\.\$' (i.e., a +|math_shift| character) is scanned. We must check to see whether this +`\.\$' is immediately followed by another, in case display math mode is +called for. + +@<Cases of |main_control| that build...@>= +case hmode+math_shift: init_math();@+break; + +@ @<Declare act...@>= +static void init_math(void) +{@+ +scaled w; /*new or partial |pre_display_size|*/ +scaled @!l; /*new |display_width|*/ +scaled @!s; /*new |display_indent|*/ +pointer @!p; /*current node when calculating |pre_display_size|*/ +pointer @!q; /*glue specification when calculating |pre_display_size|*/ +internal_font_number @!f; /*font in current |char_node|*/ +int @!n; /*scope of paragraph shape specification*/ +scaled @!v; /*|w| plus possible glue amount*/ +scaled @!d; /*increment to |v|*/ +get_token(); /*|get_x_token| would fail on \.{\\ifmmode}\thinspace!*/ +if ((cur_cmd==math_shift)&&(mode > 0)) @<Go into display math mode@>@; +else{@+back_input();@<Go into ordinary math mode@>; + } +} + +@ @<Go into ordinary math mode@>= +{@+push_math(math_shift_group);eq_word_define(int_base+cur_fam_code,-1); +if (every_math!=null) begin_token_list(every_math, every_math_text); +} + +@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or +`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; +the value of |cur_chr| is placed onto |save_stack| for safe keeping. + +@<Cases of |main_control| that build...@>= +case mmode+eq_no: if (privileged()) + if (cur_group==math_shift_group) start_eq_no(); + else off_save();@+break; + +@ @<Put each...@>= +primitive("eqno", eq_no, 0); +@!@:eq\_no\_}{\.{\\eqno} primitive@> +primitive("leqno", eq_no, 1); +@!@:leq\_no\_}{\.{\\leqno} primitive@> + +@ When \TeX\ is in display math mode, |cur_group==math_shift_group|, +so it is not necessary for the |start_eq_no| procedure to test for +this condition. + +@<Declare act...@>= +static void start_eq_no(void) +{@+saved(0)=cur_chr;incr(save_ptr); +@<Go into ordinary math mode@>; +} + +@ @<Cases of |print_cmd_chr|...@>= +case eq_no: if (chr_code==1) print_esc("leqno");@+else print_esc("eqno");@+break; + +@ @<Forbidden...@>=non_math(eq_no): + +@ When we enter display math mode, we need to call |line_break| to +process the partial paragraph that has just been interrupted by the +display. Then we can set the proper values of |display_width| and +|display_indent| and |pre_display_size|. + +@<Go into display math mode@>= +{@+if (head==tail) /*`\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}'*/ + {@+pop_nest();w=-max_dimen; + } +else{@+line_break(display_widow_penalty);@/ + @<Calculate the natural width, |w|, by which the characters of the final +line extend to the right of the reference point, plus two ems; or set |w:=max_dimen| +if the non-blank information on that line is affected by stretching or shrinking@>; + } + /*now we are in vertical mode, working on the list that will contain the display*/ +@<Calculate the length, |l|, and the shift amount, |s|, of the display lines@>; +push_math(math_shift_group);mode=mmode; +eq_word_define(int_base+cur_fam_code,-1);@/ +eq_word_define(dimen_base+pre_display_size_code, w); +eq_word_define(dimen_base+display_width_code, l); +eq_word_define(dimen_base+display_indent_code, s); +if (every_display!=null) begin_token_list(every_display, every_display_text); +if (nest_ptr==1) build_page(); +} + +@ @<Calculate the natural width, |w|, by which...@>= +v=shift_amount(just_box)+2*quad(cur_font);w=-max_dimen; +p=list_ptr(just_box); +while (p!=null) + {@+@<Let |d| be the natural width of node |p|; if the node is ``visible,'' +|goto found|; if the node is glue that stretches or shrinks, set |v:=max_dimen|@>; + if (v < max_dimen) v=v+d; + goto not_found; + found: if (v < max_dimen) + {@+v=v+d;w=v; + } + else{@+w=max_dimen;goto done; + } + not_found: p=link(p); + } +done: + +@ @<Let |d| be the natural width of node |p|...@>= +reswitch: if (is_char_node(p)) + {@+f=font(p);d=char_width(f, char_info(f, character(p))); + goto found; + } +switch (type(p)) { +case hlist_node: case vlist_node: case rule_node: {@+d=width(p);goto found; + } +case ligature_node: @<Make node |p| look like a |char_node|...@>@; +case kern_node: case math_node: d=width(p);@+break; +case glue_node: @<Let |d| be the natural width of this glue; if stretching +or shrinking, set |v:=max_dimen|; |goto found| in the case of leaders@>@;@+break; +case whatsit_node: @<Let |d| be the width of the whatsit |p|@>;@+break; +default:d=0; +} + +@ We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| +values, since such values are subject to system-dependent rounding. +System-dependent numbers are not allowed to infiltrate parameters like +|pre_display_size|, since \TeX82 is supposed to make the same decisions on all +machines. + +@<Let |d| be the natural width of this glue...@>= +{@+q=glue_ptr(p);d=width(q); +if (glue_sign(just_box)==stretching) + {@+if ((glue_order(just_box)==stretch_order(q))&&@| + (stretch(q)!=0)) + v=max_dimen; + } +else if (glue_sign(just_box)==shrinking) + {@+if ((glue_order(just_box)==shrink_order(q))&&@| + (shrink(q)!=0)) + v=max_dimen; + } +if (subtype(p) >= a_leaders) goto found; +} + +@ A displayed equation is considered to be three lines long, so we +calculate the length and offset of line number |prev_graf+2|. + +@<Calculate the length, |l|,...@>= +if (par_shape_ptr==null) + if ((hang_indent!=0)&&@| + (((hang_after >= 0)&&(prev_graf+2 > hang_after))||@| + (prev_graf+1 < -hang_after))) + {@+l=hsize-abs(hang_indent); + if (hang_indent > 0) s=hang_indent;@+else s=0; + } + else{@+l=hsize;s=0; + } +else{@+n=info(par_shape_ptr); + if (prev_graf+2 >= n) p=par_shape_ptr+2*n; + else p=par_shape_ptr+2*(prev_graf+2); + s=mem[p-1].sc;l=mem[p].sc; + } + +@ Subformulas of math formulas cause a new level of math mode to be entered, +on the semantic nest as well as the save stack. These subformulas arise in +several ways: (1)~A left brace by itself indicates the beginning of a +subformula that will be put into a box, thereby freezing its glue and +preventing line breaks. (2)~A subscript or superscript is treated as a +subformula if it is not a single character; the same applies to +the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive +initiates a subformula that will be terminated by a matching \.{\\right}. +The group codes placed on |save_stack| in these three cases are +|math_group|, |math_group|, and |math_left_group|, respectively. + +Here is the code that handles case (1); the other cases are not quite as +trivial, so we shall consider them later. + +@<Cases of |main_control| that build...@>= +case mmode+left_brace: {@+tail_append(new_noad()); + back_input();scan_math(nucleus(tail)); + } @+break; + +@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad are +broken down into subfields called |math_type| and either |info| or +|(fam, character)|. The job of |scan_math| is to figure out what to place +in one of these principal fields; it looks at the subformula that +comes next in the input, and places an encoding of that subformula +into a given word of |mem|. + +@d fam_in_range ((cur_fam >= 0)&&(cur_fam < 16)) + +@<Declare act...@>= +static void scan_math(pointer @!p) +{@+ +int c; /*math character code*/ +restart: @<Get the next non-blank non-relax...@>; +reswitch: switch (cur_cmd) { +case letter: case other_char: case char_given: {@+c=ho(math_code(cur_chr)); + if (c==0100000) + {@+@<Treat |cur_chr| as an active character@>; + goto restart; + } + } @+break; +case char_num: {@+scan_char_num();cur_chr=cur_val;cur_cmd=char_given; + goto reswitch; + } +case math_char_num: {@+scan_fifteen_bit_int();c=cur_val; + } @+break; +case math_given: c=cur_chr;@+break; +case delim_num: {@+scan_twenty_seven_bit_int();c=cur_val/010000; + } @+break; +default:@<Scan a subformula enclosed in braces and |return|@>@; +} @/ +math_type(p)=math_char;character(p)=qi(c%256); +if ((c >= var_code)&&fam_in_range) fam(p)=cur_fam; +else fam(p)=(c/256)%16; +} + +@ An active character that is an |outer_call| is allowed here. + +@<Treat |cur_chr|...@>= +{@+cur_cs=cur_chr+active_base; +cur_cmd=eq_type(cur_cs);cur_chr=equiv(cur_cs); +x_token();back_input(); +} + +@ The pointer |p| is placed on |save_stack| while a complex subformula +is being scanned. + +@<Scan a subformula...@>= +{@+back_input();scan_left_brace();@/ +saved(0)=p;incr(save_ptr);push_math(math_group);return; +} + +@ The simplest math formula is, of course, `\.{\${ }\$}', when no noads are +generated. The next simplest cases involve a single character, e.g., +`\.{\$x\$}'. Even though such cases may not seem to be very interesting, +the reader can perhaps understand how happy the author was when `\.{\$x\$}' +was first properly typeset by \TeX. The code in this section was used. +@^Knuth, Donald Ervin@> + +@<Cases of |main_control| that build...@>= +case mmode+letter: case mmode+other_char: + case mmode+char_given: + set_math_char(ho(math_code(cur_chr)));@+break; +case mmode+char_num: {@+scan_char_num();cur_chr=cur_val; + set_math_char(ho(math_code(cur_chr))); + } @+break; +case mmode+math_char_num: {@+scan_fifteen_bit_int();set_math_char(cur_val); + } @+break; +case mmode+math_given: set_math_char(cur_chr);@+break; +case mmode+delim_num: {@+scan_twenty_seven_bit_int(); + set_math_char(cur_val/010000); + } @+break; + +@ The |set_math_char| procedure creates a new noad appropriate to a given +math code, and appends it to the current mlist. However, if the math code +is sufficiently large, the |cur_chr| is treated as an active character and +nothing is appended. + +@<Declare act...@>= +static void set_math_char(int @!c) +{@+pointer p; /*the new noad*/ +if (c >= 0100000) + @<Treat |cur_chr|...@>@; +else{@+p=new_noad();math_type(nucleus(p))=math_char; + character(nucleus(p))=qi(c%256); + fam(nucleus(p))=(c/256)%16; + if (c >= var_code) + {@+if (fam_in_range) fam(nucleus(p))=cur_fam; + type(p)=ord_noad; + } + else type(p)=ord_noad+(c/010000); + link(tail)=p;tail=p; + } +} + +@ Primitive math operators like \.{\\mathop} and \.{\\underline} are given +the command code |math_comp|, supplemented by the noad type that they +generate. + +@<Put each...@>= +primitive("mathord", math_comp, ord_noad); +@!@:math\_ord\_}{\.{\\mathord} primitive@> +primitive("mathop", math_comp, op_noad); +@!@:math\_op\_}{\.{\\mathop} primitive@> +primitive("mathbin", math_comp, bin_noad); +@!@:math\_bin\_}{\.{\\mathbin} primitive@> +primitive("mathrel", math_comp, rel_noad); +@!@:math\_rel\_}{\.{\\mathrel} primitive@> +primitive("mathopen", math_comp, open_noad); +@!@:math\_open\_}{\.{\\mathopen} primitive@> +primitive("mathclose", math_comp, close_noad); +@!@:math\_close\_}{\.{\\mathclose} primitive@> +primitive("mathpunct", math_comp, punct_noad); +@!@:math\_punct\_}{\.{\\mathpunct} primitive@> +primitive("mathinner", math_comp, inner_noad); +@!@:math\_inner\_}{\.{\\mathinner} primitive@> +primitive("underline", math_comp, under_noad); +@!@:underline\_}{\.{\\underline} primitive@> +primitive("overline", math_comp, over_noad);@/ +@!@:overline\_}{\.{\\overline} primitive@> +primitive("displaylimits", limit_switch, normal); +@!@:display\_limits\_}{\.{\\displaylimits} primitive@> +primitive("limits", limit_switch, limits); +@!@:limits\_}{\.{\\limits} primitive@> +primitive("nolimits", limit_switch, no_limits); +@!@:no\_limits\_}{\.{\\nolimits} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case math_comp: switch (chr_code) { + case ord_noad: print_esc("mathord");@+break; + case op_noad: print_esc("mathop");@+break; + case bin_noad: print_esc("mathbin");@+break; + case rel_noad: print_esc("mathrel");@+break; + case open_noad: print_esc("mathopen");@+break; + case close_noad: print_esc("mathclose");@+break; + case punct_noad: print_esc("mathpunct");@+break; + case inner_noad: print_esc("mathinner");@+break; + case under_noad: print_esc("underline");@+break; + default:print_esc("overline"); + } @+break; +case limit_switch: if (chr_code==limits) print_esc("limits"); + else if (chr_code==no_limits) print_esc("nolimits"); + else print_esc("displaylimits");@+break; + +@ @<Cases of |main_control| that build...@>= +case mmode+math_comp: {@+tail_append(new_noad()); + type(tail)=cur_chr;scan_math(nucleus(tail)); + } @+break; +case mmode+limit_switch: math_limit_switch();@+break; + +@ @<Declare act...@>= +static void math_limit_switch(void) +{@+ +if (head!=tail) if (type(tail)==op_noad) + {@+subtype(tail)=cur_chr;return; + } +print_err("Limit controls must follow a math operator"); +@.Limit controls must follow...@> +help1("I'm ignoring this misplaced \\limits or \\nolimits command.");error(); +} + +@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. +The first parameter of this procedure is the |mem| address where the +delimiter is to be placed; the second tells if this delimiter follows +\.{\\radical} or not. + +@<Declare act...@>= +static void scan_delimiter(pointer @!p, bool @!r) +{@+if (r) scan_twenty_seven_bit_int(); +else{@+@<Get the next non-blank non-relax...@>; + switch (cur_cmd) { + case letter: case other_char: cur_val=del_code(cur_chr);@+break; + case delim_num: scan_twenty_seven_bit_int();@+break; + default:cur_val=-1; + } + } +if (cur_val < 0) @<Report that an invalid delimiter code is being changed +to null; set~|cur_val:=0|@>; +small_fam(p)=(cur_val/04000000)%16; +small_char(p)=qi((cur_val/010000)%256); +large_fam(p)=(cur_val/256)%16; +large_char(p)=qi(cur_val%256); +} + +@ @<Report that an invalid delimiter...@>= +{@+print_err("Missing delimiter (. inserted)"); +@.Missing delimiter...@> +help6("I was expecting to see something like `(' or `\\{' or",@/ + "`\\}' here. If you typed, e.g., `{' instead of `\\{', you",@/ + "should probably delete the `{' by typing `1' now, so that",@/ + "braces don't get unbalanced. Otherwise just proceed.",@/ + "Acceptable delimiters are characters whose \\delcode is",@/ + "nonnegative, or you can use `\\delimiter <delimiter code>'."); +back_error();cur_val=0; +} + +@ @<Cases of |main_control| that build...@>= +case mmode+radical: math_radical();@+break; + +@ @<Declare act...@>= +static void math_radical(void) +{@+tail_append(get_node(radical_noad_size)); +type(tail)=radical_noad;subtype(tail)=normal; +mem[nucleus(tail)].hh=empty_field; +mem[subscr(tail)].hh=empty_field; +mem[supscr(tail)].hh=empty_field; +scan_delimiter(left_delimiter(tail), true);scan_math(nucleus(tail)); +} + +@ @<Cases of |main_control| that build...@>= +case mmode+accent: case mmode+math_accent: math_ac();@+break; + +@ @<Declare act...@>= +static void math_ac(void) +{@+if (cur_cmd==accent) + @<Complain that the user should have said \.{\\mathaccent}@>; +tail_append(get_node(accent_noad_size)); +type(tail)=accent_noad;subtype(tail)=normal; +mem[nucleus(tail)].hh=empty_field; +mem[subscr(tail)].hh=empty_field; +mem[supscr(tail)].hh=empty_field; +math_type(accent_chr(tail))=math_char; +scan_fifteen_bit_int(); +character(accent_chr(tail))=qi(cur_val%256); +if ((cur_val >= var_code)&&fam_in_range) fam(accent_chr(tail))=cur_fam; +else fam(accent_chr(tail))=(cur_val/256)%16; +scan_math(nucleus(tail)); +} + +@ @<Complain that the user should have said \.{\\mathaccent}@>= +{@+print_err("Please use ");print_esc("mathaccent"); +print(" for accents in math mode"); +@.Please use \\mathaccent...@> +help2("I'm changing \\accent to \\mathaccent here; wish me luck.",@/ + "(Accents are not the same in formulas as they are in text.)"); +error(); +} + +@ @<Cases of |main_control| that build...@>= +case mmode+vcenter: {@+scan_spec(vcenter_group, false);normal_paragraph(); + push_nest();mode=-vmode;prev_depth=ignore_depth; + if (every_vbox!=null) begin_token_list(every_vbox, every_vbox_text); + } @+break; + +@ @<Cases of |handle...@>= +case vcenter_group: {@+end_graf();unsave();save_ptr=save_ptr-2; + p=vpack(link(head), saved(1), saved(0));pop_nest(); + tail_append(new_noad());type(tail)=vcenter_noad; + math_type(nucleus(tail))=sub_box;info(nucleus(tail))=p; + } @+break; + +@ The routine that inserts a |style_node| holds no surprises. + +@<Put each...@>= +primitive("displaystyle", math_style, display_style); +@!@:display\_style\_}{\.{\\displaystyle} primitive@> +primitive("textstyle", math_style, text_style); +@!@:text\_style\_}{\.{\\textstyle} primitive@> +primitive("scriptstyle", math_style, script_style); +@!@:script\_style\_}{\.{\\scriptstyle} primitive@> +primitive("scriptscriptstyle", math_style, script_script_style); +@!@:script\_script\_style\_}{\.{\\scriptscriptstyle} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case math_style: print_style(chr_code);@+break; + +@ @<Cases of |main_control| that build...@>= +case mmode+math_style: tail_append(new_style(cur_chr))@;@+break; +case mmode+non_script: {@+tail_append(new_glue(zero_glue)); + subtype(tail)=cond_math_glue; + } @+break; +case mmode+math_choice: append_choices();@+break; + +@ The routine that scans the four mlists of a \.{\\mathchoice} is very +much like the routine that builds discretionary nodes. + +@<Declare act...@>= +static void append_choices(void) +{@+tail_append(new_choice());incr(save_ptr);saved(-1)=0; +push_math(math_choice_group);scan_left_brace(); +} + +@ @<Cases of |handle_right_brace|...@>= +case math_choice_group: build_choices();@+break; + +@ @<Declare act...@>= +@t\4@>@<Declare the function called |fin_mlist|@>@t@>@;@/ +static void build_choices(void) +{@+ +pointer p; /*the current mlist*/ +unsave();p=fin_mlist(null); +switch (saved(-1)) { +case 0: display_mlist(tail)=p;@+break; +case 1: text_mlist(tail)=p;@+break; +case 2: script_mlist(tail)=p;@+break; +case 3: {@+script_script_mlist(tail)=p;decr(save_ptr);return; + } +} /*there are no other cases*/ +incr(saved(-1));push_math(math_choice_group);scan_left_brace(); +} + +@ Subscripts and superscripts are attached to the previous nucleus by the +@^superscripts@>@^subscripts@> +action procedure called |sub_sup|. We use the facts that |sub_mark==sup_mark+1| +and |subscr(p)==supscr(p)+1|. + +@<Cases of |main_control| that build...@>= +case mmode+sub_mark: case mmode+sup_mark: sub_sup();@+break; + +@ @<Declare act...@>= +static void sub_sup(void) +{@+small_number t; /*type of previous sub/superscript*/ +pointer @!p; /*field to be filled by |scan_math|*/ +t=empty;p=null; +if (tail!=head) if (scripts_allowed(tail)) + {@+p=supscr(tail)+cur_cmd-sup_mark; /*|supscr| or |subscr|*/ + t=math_type(p); + } +if ((p==null)||(t!=empty)) @<Insert a dummy noad to be sub/superscripted@>; +scan_math(p); +} + +@ @<Insert a dummy...@>= +{@+tail_append(new_noad()); +p=supscr(tail)+cur_cmd-sup_mark; /*|supscr| or |subscr|*/ +if (t!=empty) + {@+if (cur_cmd==sup_mark) + {@+print_err("Double superscript"); +@.Double superscript@> + help1("I treat `x^1^2' essentially like `x^1{}^2'."); + } + else{@+print_err("Double subscript"); +@.Double subscript@> + help1("I treat `x_1_2' essentially like `x_1{}_2'."); + } + error(); + } +} + +@ An operation like `\.{\\over}' causes the current mlist to go into a +state of suspended animation: |incompleat_noad| points to a |fraction_noad| +that contains the mlist-so-far as its numerator, while the denominator +is yet to come. Finally when the mlist is finished, the denominator will +go into the incompleat fraction noad, and that noad will become the +whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' +delimiters. + +@d above_code 0 /* `\.{\\above}' */ +@d over_code 1 /* `\.{\\over}' */ +@d atop_code 2 /* `\.{\\atop}' */ +@d delimited_code 3 /* `\.{\\abovewithdelims}', etc.*/ + +@<Put each...@>= +primitive("above", above, above_code);@/ +@!@:above\_}{\.{\\above} primitive@> +primitive("over", above, over_code);@/ +@!@:over\_}{\.{\\over} primitive@> +primitive("atop", above, atop_code);@/ +@!@:atop\_}{\.{\\atop} primitive@> +primitive("abovewithdelims", above, delimited_code+above_code);@/ +@!@:above\_with\_delims\_}{\.{\\abovewithdelims} primitive@> +primitive("overwithdelims", above, delimited_code+over_code);@/ +@!@:over\_with\_delims\_}{\.{\\overwithdelims} primitive@> +primitive("atopwithdelims", above, delimited_code+atop_code); +@!@:atop\_with\_delims\_}{\.{\\atopwithdelims} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case above: switch (chr_code) { + case over_code: print_esc("over");@+break; + case atop_code: print_esc("atop");@+break; + case delimited_code+above_code: print_esc("abovewithdelims");@+break; + case delimited_code+over_code: print_esc("overwithdelims");@+break; + case delimited_code+atop_code: print_esc("atopwithdelims");@+break; + default:print_esc("above"); + } @+break; + +@ @<Cases of |main_control| that build...@>= +case mmode+above: math_fraction();@+break; + +@ @<Declare act...@>= +static void math_fraction(void) +{@+small_number c; /*the type of generalized fraction we are scanning*/ +c=cur_chr; +if (incompleat_noad!=null) + @<Ignore the fraction operation and complain about this ambiguous case@>@; +else{@+incompleat_noad=get_node(fraction_noad_size); + type(incompleat_noad)=fraction_noad; + subtype(incompleat_noad)=normal; + math_type(numerator(incompleat_noad))=sub_mlist; + info(numerator(incompleat_noad))=link(head); + mem[denominator(incompleat_noad)].hh=empty_field; + mem[left_delimiter(incompleat_noad)].qqqq=null_delimiter; + mem[right_delimiter(incompleat_noad)].qqqq=null_delimiter;@/ + link(head)=null;tail=head; + @<Use code |c| to distinguish between generalized fractions@>; + } +} + +@ @<Use code |c|...@>= +if (c >= delimited_code) + {@+scan_delimiter(left_delimiter(incompleat_noad), false); + scan_delimiter(right_delimiter(incompleat_noad), false); + } +switch (c%delimited_code) { +case above_code: {@+scan_normal_dimen; + thickness(incompleat_noad)=cur_val; + } @+break; +case over_code: thickness(incompleat_noad)=default_code;@+break; +case atop_code: thickness(incompleat_noad)=0; +} /*there are no other cases*/ + +@ @<Ignore the fraction...@>= +{@+if (c >= delimited_code) + {@+scan_delimiter(garbage, false);scan_delimiter(garbage, false); + } +if (c%delimited_code==above_code) scan_normal_dimen; +print_err("Ambiguous; you need another { and }"); +@.Ambiguous...@> +help3("I'm ignoring this fraction specification, since I don't",@/ + "know whether a construction like `x \\over y \\over z'",@/ + "means `{x \\over y} \\over z' or `x \\over {y \\over z}'."); +error(); +} + +@ At the end of a math formula or subformula, the |fin_mlist| routine is +called upon to return a pointer to the newly completed mlist, and to +pop the nest back to the enclosing semantic level. The parameter to +|fin_mlist|, if not null, points to a |right_noad| that ends the +current mlist; this |right_noad| has not yet been appended. + +@<Declare the function called |fin_mlist|@>= +static pointer fin_mlist(pointer @!p) +{@+pointer q; /*the mlist to return*/ +if (incompleat_noad!=null) @<Compleat the incompleat noad@>@; +else{@+link(tail)=p;q=link(head); + } +pop_nest();return q; +} + +@ @<Compleat...@>= +{@+math_type(denominator(incompleat_noad))=sub_mlist; +info(denominator(incompleat_noad))=link(head); +if (p==null) q=incompleat_noad; +else{@+q=info(numerator(incompleat_noad)); + if ((type(q)!=left_noad)||(delim_ptr==null)) confusion("right"); +@:this can't happen right}{\quad right@> + info(numerator(incompleat_noad))=link(delim_ptr); + link(delim_ptr)=incompleat_noad;link(incompleat_noad)=p; + } +} + +@ Now at last we're ready to see what happens when a right brace occurs +in a math formula. Two special cases are simplified here: Braces are effectively +removed when they surround a single Ord without sub/superscripts, or when they +surround an accent that is the nucleus of an Ord atom. + +@<Cases of |handle...@>= +case math_group: {@+unsave();decr(save_ptr);@/ + math_type(saved(0))=sub_mlist;p=fin_mlist(null);info(saved(0))=p; + if (p!=null) if (link(p)==null) + if (type(p)==ord_noad) + {@+if (math_type(subscr(p))==empty) + if (math_type(supscr(p))==empty) + {@+mem[saved(0)].hh=mem[nucleus(p)].hh; + free_node(p, noad_size); + } + } + else if (type(p)==accent_noad) if (saved(0)==nucleus(tail)) + if (type(tail)==ord_noad) @<Replace the tail of the list by |p|@>; + } @+break; + +@ @<Replace the tail...@>= +{@+q=head;while (link(q)!=tail) q=link(q); +link(q)=p;free_node(tail, noad_size);tail=p; +} + +@ We have dealt with all constructions of math mode except `\.{\\left}' and +`\.{\\right}', so the picture is completed by the following sections of +the program. + +@<Put each...@>= +primitive("left", left_right, left_noad); +@!@:left\_}{\.{\\left} primitive@> +primitive("right", left_right, right_noad); +@!@:right\_}{\.{\\right} primitive@> +text(frozen_right)=text(cur_val);eqtb[frozen_right]=eqtb[cur_val]; + +@ @<Cases of |print_cmd_chr|...@>= +case left_right: if (chr_code==left_noad) print_esc("left") +@/@<Cases of |left_right| for |print_cmd_chr|@>;@/ +else print_esc("right");@+break; + +@ @<Cases of |main_control| that build...@>= +case mmode+left_right: math_left_right();@+break; + +@ @<Declare act...@>= +static void math_left_right(void) +{@+small_number t; /*|left_noad| or |right_noad|*/ +pointer @!p; /*new noad*/ +pointer @!q; /*resulting mlist*/ +t=cur_chr; +if ((t!=left_noad)&&(cur_group!=math_left_group)) + @<Try to recover from mismatched \.{\\right}@>@; +else{@+p=new_noad();type(p)=t; + scan_delimiter(delimiter(p), false); + if (t==middle_noad) + {@+type(p)=right_noad;subtype(p)=middle_noad; + } + if (t==left_noad) q=p; + else{@+q=fin_mlist(p);unsave(); /*end of |math_left_group|*/ + } + if (t!=right_noad) + {@+push_math(math_left_group);link(head)=q;tail=p; + delim_ptr=p; + } + else{@+ + tail_append(new_noad());type(tail)=inner_noad; + math_type(nucleus(tail))=sub_mlist; + info(nucleus(tail))=q; + } + } +} + +@ @<Try to recover from mismatch...@>= +{@+if (cur_group==math_shift_group) + {@+scan_delimiter(garbage, false); + print_err("Extra "); + if (t==middle_noad) + {@+print_esc("middle"); +@.Extra \\middle.@> + help1("I'm ignoring a \\middle that had no matching \\left."); + } + else{@+print_esc("right"); +@.Extra \\right.@> + help1("I'm ignoring a \\right that had no matching \\left."); + } + error(); + } +else off_save(); +} + +@ Here is the only way out of math mode. + +@<Cases of |main_control| that build...@>= +case mmode+math_shift: if (cur_group==math_shift_group) after_math(); + else off_save();@+break; + +@ @<Declare act...@>= +static void after_math(void) +{@+bool l; /*`\.{\\leqno}' instead of `\.{\\eqno}'*/ +bool @!danger; /*not enough symbol fonts are present*/ +int @!m; /*|mmode| or |-mmode|*/ +pointer @!p; /*the formula*/ +pointer @!a; /*box containing equation number*/ +@<Local variables for finishing a displayed formula@>@; +danger=false; +@<Check that the necessary fonts for math symbols are present; if not, flush +the current math lists and set |danger:=true|@>; +m=mode;l=false;p=fin_mlist(null); /*this pops the nest*/ +if (mode==-m) /*end of equation number*/ + {@+@<Check that another \.\$ follows@>; + cur_mlist=p;cur_style=text_style;mlist_penalties=false; + mlist_to_hlist();a=hpack(link(temp_head), natural); + unsave();decr(save_ptr); /*now |cur_group==math_shift_group|*/ + if (saved(0)==1) l=true; + danger=false; + @<Check that the necessary fonts for math symbols are present; if not, flush +the current math lists and set |danger:=true|@>; + m=mode;p=fin_mlist(null); + } +else a=null; +if (m < 0) @<Finish math in text@>@; +else{@+if (a==null) @<Check that another \.\$ follows@>; + @<Finish displayed math@>; + } +} + +@ @<Check that the necessary fonts...@>= +if ((font_params[fam_fnt(2+text_size)] < total_mathsy_params)||@| + (font_params[fam_fnt(2+script_size)] < total_mathsy_params)||@| + (font_params[fam_fnt(2+script_script_size)] < total_mathsy_params)) + {@+print_err("Math formula deleted: Insufficient symbol fonts");@/ +@.Math formula deleted...@> + help3("Sorry, but I can't typeset math unless \\textfont 2",@/ + "and \\scriptfont 2 and \\scriptscriptfont 2 have all",@/ + "the \\fontdimen values needed in math symbol fonts."); + error();flush_math();danger=true; + } +else if ((font_params[fam_fnt(3+text_size)] < total_mathex_params)||@| + (font_params[fam_fnt(3+script_size)] < total_mathex_params)||@| + (font_params[fam_fnt(3+script_script_size)] < total_mathex_params)) + {@+print_err("Math formula deleted: Insufficient extension fonts");@/ + help3("Sorry, but I can't typeset math unless \\textfont 3",@/ + "and \\scriptfont 3 and \\scriptscriptfont 3 have all",@/ + "the \\fontdimen values needed in math extension fonts."); + error();flush_math();danger=true; + } + +@ The |unsave| is done after everything else here; hence an appearance of +`\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these +particular \.\$'s. This is consistent with the conventions of +`\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the +space above that display. + +@<Finish math in text@>= +{@+tail_append(new_math(math_surround, before)); +cur_mlist=p;cur_style=text_style;mlist_penalties=(mode > 0);mlist_to_hlist(); +link(tail)=link(temp_head); +while (link(tail)!=null) tail=link(tail); +tail_append(new_math(math_surround, after)); +space_factor=1000;unsave(); +} + +@ \TeX\ gets to the following part of the program when the first `\.\$' ending +a display has been scanned. + +@<Check that another \.\$ follows@>= +{@+get_x_token(); +if (cur_cmd!=math_shift) + {@+print_err("Display math should end with $$"); +@.Display math...with \$\$@> + help2("The `$' that I just saw supposedly matches a previous `$$'.",@/ + "So I shall assume that you typed `$$' both times."); + back_error(); + } +} + +@ We have saved the worst for last: The fussiest part of math mode processing +occurs when a displayed formula is being centered and placed with an optional +equation number. + +@<Local variables for finishing...@>= +pointer @!b; /*box containing the equation*/ +scaled @!w; /*width of the equation*/ +scaled @!z; /*width of the line*/ +scaled @!e; /*width of equation number*/ +scaled @!q; /*width of equation number plus space to separate from equation*/ +scaled @!d; /*displacement of equation in the line*/ +scaled @!s; /*move the line right this much*/ +small_number @!g1, @!g2; /*glue parameter codes for before and after*/ +pointer @!r; /*kern node used to position the display*/ +pointer @!t; /*tail of adjustment list*/ + +@ At this time |p| points to the mlist for the formula; |a| is either +|null| or it points to a box containing the equation number; and we are in +vertical mode (or internal vertical mode). + +@<Finish displayed math@>= +cur_mlist=p;cur_style=display_style;mlist_penalties=false; +mlist_to_hlist();p=link(temp_head);@/ +adjust_tail=adjust_head;b=hpack(p, natural);p=list_ptr(b); +t=adjust_tail;adjust_tail=null;@/ +w=width(b);z=display_width;s=display_indent; +if ((a==null)||danger) + {@+e=0;q=0; + } +else{@+e=width(a);q=e+math_quad(text_size); + } +if (w+q > z) + @<Squeeze the equation as much as possible; if there is an equation number +that should go on a separate line by itself, set~|e:=0|@>; +@<Determine the displacement, |d|, of the left edge of the equation, with +respect to the line size |z|, assuming that |l=false|@>; +@<Append the glue or equation number preceding the display@>; +@<Append the display and perhaps also the equation number@>; +@<Append the glue or equation number following the display@>; +resume_after_display() + +@ @<Declare act...@>= +static void resume_after_display(void) +{@+if (cur_group!=math_shift_group) confusion("display"); +@:this can't happen display}{\quad display@> +unsave();prev_graf=prev_graf+3; +push_nest();mode=hmode;space_factor=1000;set_cur_lang;clang=cur_lang; +prev_graf=(norm_min(left_hyphen_min)*0100+norm_min(right_hyphen_min)) + *0200000+cur_lang; +@<Scan an optional space@>; +if (nest_ptr==1) build_page(); +} + +@ The user can force the equation number to go on a separate line +by causing its width to be zero. + +@<Squeeze the equation as much as possible...@>= +{@+if ((e!=0)&&((w-total_shrink[normal]+q <= z)||@| + (total_shrink[fil]!=0)||(total_shrink[fill]!=0)|| + (total_shrink[filll]!=0))) + {@+free_node(b, box_node_size); + b=hpack(p, z-q, exactly); + } +else{@+e=0; + if (w > z) + {@+free_node(b, box_node_size); + b=hpack(p, z, exactly); + } + } +w=width(b); +} + +@ We try first to center the display without regard to the existence of +the equation number. If that would make it too close (where ``too close'' +means that the space between display and equation number is less than the +width of the equation number), we either center it in the remaining space +or move it as far from the equation number as possible. The latter alternative +is taken only if the display begins with glue, since we assume that the +user put glue there to control the spacing precisely. + +@<Determine the displacement, |d|, of the left edge of the equation...@>= +d=half(z-w); +if ((e > 0)&&(d < 2*e)) /*too close*/ + {@+d=half(z-w-e); + if (p!=null) if (!is_char_node(p)) if (type(p)==glue_node) d=0; + } + +@ If the equation number is set on a line by itself, either before or +after the formula, we append an infinite penalty so that no page break will +separate the display from its number; and we use the same size and +displacement for all three potential lines of the display, even though +`\.{\\parshape}' may specify them differently. + +@<Append the glue or equation number preceding the display@>= +tail_append(new_penalty(pre_display_penalty));@/ +if ((d+s <= pre_display_size)||l) /*not enough clearance*/ + {@+g1=above_display_skip_code;g2=below_display_skip_code; + } +else{@+g1=above_display_short_skip_code; + g2=below_display_short_skip_code; + } +if (l&&(e==0)) /*it follows that |type(a)==hlist_node|*/ + {@+shift_amount(a)=s;append_to_vlist(a); + tail_append(new_penalty(inf_penalty)); + } +else tail_append(new_param_glue(g1)) + +@ @<Append the display and perhaps also the equation number@>= +if (e!=0) + {@+r=new_kern(z-w-e-d); + if (l) + {@+link(a)=r;link(r)=b;b=a;d=0; + } + else{@+link(b)=r;link(r)=a; + } + b=hpack(b, natural); + } +shift_amount(b)=s+d;append_to_vlist(b) + +@ @<Append the glue or equation number following the display@>= +if ((a!=null)&&(e==0)&&!l) + {@+tail_append(new_penalty(inf_penalty)); + shift_amount(a)=s+z-width(a); + append_to_vlist(a); + g2=0; + } +if (t!=adjust_head) /*migrating material comes after equation number*/ + {@+link(tail)=link(adjust_head);tail=t; + } +tail_append(new_penalty(post_display_penalty)); +if (g2 > 0) tail_append(new_param_glue(g2)) + +@ When \.{\\halign} appears in a display, the alignment routines operate +essentially as they do in vertical mode. Then the following program is +activated, with |p| and |q| pointing to the beginning and end of the +resulting list, and with |aux_save| holding the |prev_depth| value. + +@<Finish an alignment in a display@>= +{@+do_assignments(); +if (cur_cmd!=math_shift) @<Pontificate about improper alignment in display@>@; +else@<Check that another \.\$ follows@>; +pop_nest(); +tail_append(new_penalty(pre_display_penalty)); +tail_append(new_param_glue(above_display_skip_code)); +link(tail)=p; +if (p!=null) tail=q; +tail_append(new_penalty(post_display_penalty)); +tail_append(new_param_glue(below_display_skip_code)); +prev_depth=aux_save.sc;resume_after_display(); +} + +@ @<Pontificate...@>= +{@+print_err("Missing $$ inserted"); +@.Missing {\$\$} inserted@> +help2("Displays can use special alignments (like \\eqalignno)",@/ + "only if nothing but the alignment itself is between $$'s."); +back_error(); +} + +@* Mode-independent processing. +The long |main_control| procedure has now been fully specified, except for +certain activities that are independent of the current mode. These activities +do not change the current vlist or hlist or mlist; if they change anything, +it is the value of a parameter or the meaning of a control sequence. + +Assignments to values in |eqtb| can be global or local. Furthermore, a +control sequence can be defined to be `\.{\\long}', `\.{\\protected}', +or `\.{\\outer}', and it might or might not be expanded. The prefixes +`\.{\\global}', `\.{\\long}', `\.{\\protected}', +and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric +codes, making it possible to accumulate the union of all specified prefixes +by adding the corresponding codes. (\PASCAL's |set| operations could also +have been used.) + +@<Put each...@>= +primitive("long", prefix, 1); +@!@:long\_}{\.{\\long} primitive@> +primitive("outer", prefix, 2); +@!@:outer\_}{\.{\\outer} primitive@> +primitive("global", prefix, 4); +@!@:global\_}{\.{\\global} primitive@> +primitive("def", def, 0); +@!@:def\_}{\.{\\def} primitive@> +primitive("gdef", def, 1); +@!@:gdef\_}{\.{\\gdef} primitive@> +primitive("edef", def, 2); +@!@:edef\_}{\.{\\edef} primitive@> +primitive("xdef", def, 3); +@!@:xdef\_}{\.{\\xdef} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case prefix: if (chr_code==1) print_esc("long"); + else if (chr_code==2) print_esc("outer") + @/@<Cases of |prefix| for |print_cmd_chr|@>;@/ + else print_esc("global");@+break; +case def: if (chr_code==0) print_esc("def"); + else if (chr_code==1) print_esc("gdef"); + else if (chr_code==2) print_esc("edef"); + else print_esc("xdef");@+break; + +@ Every prefix, and every command code that might or might not be prefixed, +calls the action procedure |prefixed_command|. This routine accumulates +a sequence of prefixes until coming to a non-prefix, then it carries out +the command. + +@<Cases of |main_control| that don't...@>= +any_mode(toks_register): +any_mode(assign_toks): +any_mode(assign_int): +any_mode(assign_dimen): +any_mode(assign_glue): +any_mode(assign_mu_glue): +any_mode(assign_font_dimen): +any_mode(assign_font_int): +any_mode(set_aux): +any_mode(set_prev_graf): +any_mode(set_page_dimen): +any_mode(set_page_int): +any_mode(set_box_dimen): +any_mode(set_shape): +any_mode(def_code): +any_mode(def_family): +any_mode(set_font): +any_mode(def_font): +any_mode(internal_register): +any_mode(advance): +any_mode(multiply): +any_mode(divide): +any_mode(prefix): +any_mode(let): +any_mode(shorthand_def): +any_mode(read_to_cs): +any_mode(def): +any_mode(set_box): +any_mode(hyph_data): +any_mode(set_interaction): prefixed_command();@+break; + +@ If the user says, e.g., `\.{\\global\\global}', the redundancy is +silently accepted. + +@<Declare act...@>= +@t\4@>@<Declare subprocedures for |prefixed_command|@>@t@>@;@/ +static void prefixed_command(void) +{@+ +small_number a; /*accumulated prefix codes so far*/ +internal_font_number @!f; /*identifies a font*/ +int @!j; /*index into a \.{\\parshape} specification*/ +font_index @!k; /*index into |font_info|*/ +pointer @!p, @!q; /*for temporary short-term use*/ +int @!n; /*ditto*/ +bool @!e; /*should a definition be expanded? or was \.{\\let} not done?*/ +a=0; +while (cur_cmd==prefix) + {@+if (!odd(a/cur_chr)) a=a+cur_chr; + @<Get the next non-blank non-relax...@>; + if (cur_cmd <= max_non_prefixed_command) + @<Discard erroneous prefixes and |return|@>; + if (tracing_commands > 2) if (eTeX_ex) show_cur_cmd_chr(); + } +@<Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant@>; +@<Adjust \(f)for the setting of \.{\\globaldefs}@>; +switch (cur_cmd) { +@t\4@>@<Assignments@>@; +default:confusion("prefix"); +@:this can't happen prefix}{\quad prefix@> +} +done: @<Insert a token saved by \.{\\afterassignment}, if any@>; +} + +@ @<Discard erroneous...@>= +{@+print_err("You can't use a prefix with `"); +@.You can't use a prefix with x@> +print_cmd_chr(cur_cmd, cur_chr);print_char('\''); +help1("I'll pretend you didn't say \\long or \\outer or \\global."); +if (eTeX_ex) help_line[0]=@| + "I'll pretend you didn't say \\long or \\outer or \\global or \\protected."; +back_error();return; +} + +@ @<Discard the prefixes...@>= +if (a >= 8) + {@+j=protected_token;a=a-8; + } +else j=0; +if ((cur_cmd!=def)&&((a%4!=0)||(j!=0))) + {@+print_err("You can't use `");print_esc("long");print("' or `"); + print_esc("outer"); + help1("I'll pretend you didn't say \\long or \\outer here."); + if (eTeX_ex) + {@+help_line[0]=@| + "I'll pretend you didn't say \\long or \\outer or \\protected here."; + print("' or `");print_esc("protected"); + } + print("' with `"); +@.You can't use \\long...@> + print_cmd_chr(cur_cmd, cur_chr);print_char('\''); + error(); + } + +@ The previous routine does not have to adjust |a| so that |a%4==0|, +since the following routines test for the \.{\\global} prefix as follows. + +@d global (a >= 4) +@d define(A, B, C) if (global) geq_define(A, B, C);@+else eq_define(A, B, C) +@d word_define(A, B) if (global) geq_word_define(A, B);@+else eq_word_define(A, B) + +@<Adjust \(f)for the setting of \.{\\globaldefs}@>= +if (global_defs!=0) + if (global_defs < 0) + {@+if (global) a=a-4; + } + else{@+if (!global) a=a+4; + } + +@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or +something similar, the |get_r_token| routine will substitute a special +control sequence for a token that is not redefinable. + +@<Declare subprocedures for |prefixed_command|@>= +static void get_r_token(void) +{@+ +restart: @/do@+{get_token(); +}@+ while (!(cur_tok!=space_token)); +if ((cur_cs==0)||(cur_cs > frozen_control_sequence)) + {@+print_err("Missing control sequence inserted"); +@.Missing control...@> + help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.",@/ + "I've inserted an inaccessible control sequence so that your",@/ + "definition will be completed without mixing me up too badly.",@/ + "You can recover graciously from this error, if you're",@/ + "careful; see exercise 27.2 in The TeXbook."); +@:TeXbook}{\sl The \TeX book@> + if (cur_cs==0) back_input(); + cur_tok=cs_token_flag+frozen_protection;ins_error();goto restart; + } +} + +@ @<Initialize table entries...@>= +text(frozen_protection)=s_no("inaccessible"); +@.inaccessible@> + +@ Here's an example of the way many of the following routines operate. +(Unfortunately, they aren't all as simple as this.) + +@<Assignments@>= +case set_font: define(cur_font_loc, data, cur_chr);@+break; + +@ When a |def| command has been scanned, +|cur_chr| is odd if the definition is supposed to be global, and +|cur_chr >= 2| if the definition is supposed to be expanded. + +@<Assignments@>= +case def: {@+uint32_t def_fl; + if (odd(cur_chr)&&!global&&(global_defs >= 0)) a=a+4; + e=(cur_chr >= 2);get_r_token();p=cur_cs; def_fl=cur_file_line; + q=scan_toks(true, e); + if (j!=0) + {@+q=get_avail();info(q)=j;link(q)=link(def_ref); + link(def_ref)=q; + } + define(p, call+(a%4), def_ref); + fl_mem[def_ref]=def_fl; + } @+break; + +@ Both \.{\\let} and \.{\\futurelet} share the command code |let|. + +@<Put each...@>= +primitive("let", let, normal);@/ +@!@:let\_}{\.{\\let} primitive@> +primitive("futurelet", let, normal+1);@/ +@!@:future\_let\_}{\.{\\futurelet} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case let: if (chr_code!=normal) print_esc("futurelet");@+else print_esc("let");@+break; + +@ @<Assignments@>= +case let: {@+n=cur_chr; + get_r_token();p=cur_cs; + if (n==normal) + {@+@/do@+{get_token(); + }@+ while (!(cur_cmd!=spacer)); + if (cur_tok==other_token+'=') + {@+get_token(); + if (cur_cmd==spacer) get_token(); + } + } + else{@+get_token();q=cur_tok;get_token();back_input(); + cur_tok=q;back_input(); /*look ahead, then back up*/ + } /*note that |back_input| doesn't affect |cur_cmd|, |cur_chr|*/ + if (cur_cmd >= call) add_token_ref(cur_chr); + else if ((cur_cmd==internal_register)||(cur_cmd==toks_register)) + if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max)) + add_sa_ref(cur_chr); + define(p, cur_cmd, cur_chr); + } @+break; + +@ A \.{\\chardef} creates a control sequence whose |cmd| is |char_given|; +a \.{\\mathchardef} creates a control sequence whose |cmd| is |math_given|; +and the corresponding |chr| is the character code or math code. A \.{\\countdef} +or \.{\\dimendef} or \.{\\skipdef} or \.{\\muskipdef} creates a control +sequence whose |cmd| is |assign_int| or \dots\ or |assign_mu_glue|, and the +corresponding |chr| is the |eqtb| location of the internal register in question. + +@d char_def_code 0 /*|shorthand_def| for \.{\\chardef}*/ +@d math_char_def_code 1 /*|shorthand_def| for \.{\\mathchardef}*/ +@d count_def_code 2 /*|shorthand_def| for \.{\\countdef}*/ +@d dimen_def_code 3 /*|shorthand_def| for \.{\\dimendef}*/ +@d skip_def_code 4 /*|shorthand_def| for \.{\\skipdef}*/ +@d mu_skip_def_code 5 /*|shorthand_def| for \.{\\muskipdef}*/ +@d toks_def_code 6 /*|shorthand_def| for \.{\\toksdef}*/ + +@<Put each...@>= +primitive("chardef", shorthand_def, char_def_code);@/ +@!@:char\_def\_}{\.{\\chardef} primitive@> +primitive("mathchardef", shorthand_def, math_char_def_code);@/ +@!@:math\_char\_def\_}{\.{\\mathchardef} primitive@> +primitive("countdef", shorthand_def, count_def_code);@/ +@!@:count\_def\_}{\.{\\countdef} primitive@> +primitive("dimendef", shorthand_def, dimen_def_code);@/ +@!@:dimen\_def\_}{\.{\\dimendef} primitive@> +primitive("skipdef", shorthand_def, skip_def_code);@/ +@!@:skip\_def\_}{\.{\\skipdef} primitive@> +primitive("muskipdef", shorthand_def, mu_skip_def_code);@/ +@!@:mu\_skip\_def\_}{\.{\\muskipdef} primitive@> +primitive("toksdef", shorthand_def, toks_def_code);@/ +@!@:toks\_def\_}{\.{\\toksdef} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case shorthand_def: switch (chr_code) { + case char_def_code: print_esc("chardef");@+break; + case math_char_def_code: print_esc("mathchardef");@+break; + case count_def_code: print_esc("countdef");@+break; + case dimen_def_code: print_esc("dimendef");@+break; + case skip_def_code: print_esc("skipdef");@+break; + case mu_skip_def_code: print_esc("muskipdef");@+break; + default:print_esc("toksdef"); + } @+break; +case char_given: {@+print_esc("char");print_hex(chr_code); + } @+break; +case math_given: {@+print_esc("mathchar");print_hex(chr_code); + } @+break; + +@ We temporarily define |p| to be |relax|, so that an occurrence of |p| +while scanning the definition will simply stop the scanning instead of +producing an ``undefined control sequence'' error or expanding the +previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. + +@<Assignments@>= +case shorthand_def: {@+n=cur_chr;get_r_token();p=cur_cs;define(p, relax, 256); + scan_optional_equals(); + switch (n) { + case char_def_code: {@+scan_char_num();define(p, char_given, cur_val); + } @+break; + case math_char_def_code: {@+scan_fifteen_bit_int();define(p, math_given, cur_val); + } @+break; + default:{@+scan_register_num(); + if (cur_val > 255) + {@+j=n-count_def_code; /*|int_val dotdot box_val|*/ + if (j > mu_val) j=tok_val; /*|int_val dotdot mu_val| or |tok_val|*/ + find_sa_element(j, cur_val, true);add_sa_ref(cur_ptr); + if (j==tok_val) j=toks_register;@+else j=internal_register; + define(p, j, cur_ptr); + } + else + switch (n) { + case count_def_code: define(p, assign_int, count_base+cur_val);@+break; + case dimen_def_code: define(p, assign_dimen, scaled_base+cur_val);@+break; + case skip_def_code: define(p, assign_glue, skip_base+cur_val);@+break; + case mu_skip_def_code: define(p, assign_mu_glue, mu_skip_base+cur_val);@+break; + case toks_def_code: define(p, assign_toks, toks_base+cur_val); + } /*there are no other cases*/ + } + } + } @+break; + +@ @<Assignments@>= +case read_to_cs: {@+j=cur_chr;scan_int();n=cur_val; + if (!scan_keyword("to")) +@.to@> + {@+print_err("Missing `to' inserted"); +@.Missing `to'...@> + help2("You should have said `\\read<number> to \\cs'.",@/ + "I'm going to look for the \\cs now.");error(); + } + get_r_token(); + p=cur_cs;read_toks(n, p, j);define(p, call, cur_val); + } @+break; + +@ The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive +their values in the following way. (For safety's sake, we place an +enclosing pair of braces around an \.{\\output} list.) + +@<Assignments@>= +case toks_register: case assign_toks: {@+uint32_t def_fl=cur_file_line; q=cur_cs; + e=false; /*just in case, will be set |true| for sparse array elements*/ + if (cur_cmd==toks_register) + if (cur_chr==mem_bot) + {@+scan_register_num(); + if (cur_val > 255) + {@+find_sa_element(tok_val, cur_val, true); + cur_chr=cur_ptr;e=true; + } + else cur_chr=toks_base+cur_val; + } + else e=true; + p=cur_chr; /*|p==every_par_loc| or |output_routine_loc| or \dots*/ + scan_optional_equals(); + @<Get the next non-blank non-relax non-call token@>; + if (cur_cmd!=left_brace) @<If the right-hand side is a token parameter or +token register, finish the assignment and |goto done|@>; + back_input();cur_cs=q;q=scan_toks(false, false); + if (link(def_ref)==null) /*empty list: revert to the default*/ + {@+sa_define(p, null, p, undefined_cs, null);free_avail(def_ref); + } + else{@+if ((p==output_routine_loc)&&!e) /*enclose in curlies*/ + {@+link(q)=get_avail();q=link(q); + info(q)=right_brace_token+'}'; + fl_mem[q]=FILE_LINE(system_file, system_insert); + q=get_avail();info(q)=left_brace_token+'{'; + fl_mem[q]=FILE_LINE(system_file, system_insert); + link(q)=link(def_ref);link(def_ref)=q; + } + sa_define(p, def_ref, p, call, def_ref); + fl_mem[def_ref]=def_fl; + } + } @+break; + +@ @<If the right-hand side is a token parameter...@>= +if ((cur_cmd==toks_register)||(cur_cmd==assign_toks)) + {@+if (cur_cmd==toks_register) + if (cur_chr==mem_bot) + {@+scan_register_num(); + if (cur_val < 256) q=equiv(toks_base+cur_val); + else{@+find_sa_element(tok_val, cur_val, false); + if (cur_ptr==null) q=null; + else q=sa_ptr(cur_ptr); + } + } + else q=sa_ptr(cur_chr); + else q=equiv(cur_chr); + if (q==null) sa_define(p, null, p, undefined_cs, null); + else{@+add_token_ref(q);sa_define(p, q, p, call, q); + } + goto done; + } + +@ Similar routines are used to assign values to the numeric parameters. + +@<Assignments@>= +case assign_int: {@+p=cur_chr;scan_optional_equals();scan_int(); + word_define(p, cur_val); + } @+break; +case assign_dimen: {@+p=cur_chr;scan_optional_equals(); + scan_normal_dimen;word_define(p, cur_val); + } @+break; +case assign_glue: case assign_mu_glue: {@+p=cur_chr;n=cur_cmd;scan_optional_equals(); + if (n==assign_mu_glue) scan_glue(mu_val);@+else scan_glue(glue_val); + trap_zero_glue(); + define(p, glue_ref, cur_val); + } @+break; + +@ When a glue register or parameter becomes zero, it will always point to +|zero_glue| because of the following procedure. (Exception: The tabskip +glue isn't trapped while preambles are being scanned.) + +@<Declare subprocedures for |prefixed_command|@>= +static void trap_zero_glue(void) +{@+if ((width(cur_val)==0)&&(stretch(cur_val)==0)&&(shrink(cur_val)==0)) + {@+add_glue_ref(zero_glue); + delete_glue_ref(cur_val);cur_val=zero_glue; + } +} + +@ The various character code tables are changed by the |def_code| commands, +and the font families are declared by |def_family|. + +@<Put each...@>= +primitive("catcode", def_code, cat_code_base); +@!@:cat\_code\_}{\.{\\catcode} primitive@> +primitive("mathcode", def_code, math_code_base); +@!@:math\_code\_}{\.{\\mathcode} primitive@> +primitive("lccode", def_code, lc_code_base); +@!@:lc\_code\_}{\.{\\lccode} primitive@> +primitive("uccode", def_code, uc_code_base); +@!@:uc\_code\_}{\.{\\uccode} primitive@> +primitive("sfcode", def_code, sf_code_base); +@!@:sf\_code\_}{\.{\\sfcode} primitive@> +primitive("delcode", def_code, del_code_base); +@!@:del\_code\_}{\.{\\delcode} primitive@> +primitive("textfont", def_family, math_font_base); +@!@:text\_font\_}{\.{\\textfont} primitive@> +primitive("scriptfont", def_family, math_font_base+script_size); +@!@:script\_font\_}{\.{\\scriptfont} primitive@> +primitive("scriptscriptfont", def_family, math_font_base+script_script_size); +@!@:script\_script\_font\_}{\.{\\scriptscriptfont} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case def_code: if (chr_code==cat_code_base) print_esc("catcode"); + else if (chr_code==math_code_base) print_esc("mathcode"); + else if (chr_code==lc_code_base) print_esc("lccode"); + else if (chr_code==uc_code_base) print_esc("uccode"); + else if (chr_code==sf_code_base) print_esc("sfcode"); + else print_esc("delcode");@+break; +case def_family: print_size(chr_code-math_font_base);@+break; + +@ The different types of code values have different legal ranges; the +following program is careful to check each case properly. + +@<Assignments@>= +case def_code: {@+@<Let |n| be the largest legal code value, based on |cur_chr|@>; + p=cur_chr;scan_char_num();p=p+cur_val;scan_optional_equals(); + scan_int(); + if (((cur_val < 0)&&(p < del_code_base))||(cur_val > n)) + {@+print_err("Invalid code (");print_int(cur_val); +@.Invalid code@> + if (p < del_code_base) print("), should be in the range 0.."); + else print("), should be at most "); + print_int(n); + help1("I'm going to use 0 instead of that illegal code value.");@/ + error();cur_val=0; + } + if (p < math_code_base) define(p, data, cur_val); + else if (p < del_code_base) define(p, data, hi(cur_val)); + else word_define(p, cur_val); + } @+break; + +@ @<Let |n| be the largest...@>= +if (cur_chr==cat_code_base) n=max_char_code; +else if (cur_chr==math_code_base) n=0100000; +else if (cur_chr==sf_code_base) n=077777; +else if (cur_chr==del_code_base) n=077777777; +else n=255 + +@ @<Assignments@>= +case def_family: {@+p=cur_chr;scan_four_bit_int();p=p+cur_val; + scan_optional_equals();scan_font_ident();define(p, data, cur_val); + } @+break; + +@ Next we consider changes to \TeX's numeric registers. + +@<Assignments@>= +case internal_register: case advance: + case multiply: case divide: do_register_command(a);@+break; + +@ We use the fact that |internal_register < advance < multiply < divide|. + +@<Declare subprocedures for |prefixed_command|@>= +static void do_register_command(small_number @!a) +{@+ +pointer l, @!q, @!r, @!s; /*for list manipulation*/ +int @!p; /*type of register involved*/ +bool @!e; /*does |l| refer to a sparse array element?*/ +int @!w; /*integer or dimen value of |l|*/ +q=cur_cmd; +e=false; /*just in case, will be set |true| for sparse array elements*/ +@<Compute the register location |l| and its type |p|; but |return| if invalid@>; +if (q==internal_register) scan_optional_equals(); +else if (scan_keyword("by")) do_nothing; /*optional `\.{by}'*/ +@.by@> +arith_error=false; +if (q < multiply) @<Compute result of |register| or |advance|, put it in |cur_val|@>@; +else@<Compute result of |multiply| or |divide|, put it in |cur_val|@>; +if (arith_error) + {@+print_err("Arithmetic overflow"); +@.Arithmetic overflow@> + help2("I can't carry out that multiplication or division,",@/ + "since the result is out of range."); + if (p >= glue_val) delete_glue_ref(cur_val); + error();return; + } +if (p < glue_val) sa_word_define(l, cur_val); +else{@+trap_zero_glue();sa_define(l, cur_val, l, glue_ref, cur_val); + } +} + +@ Here we use the fact that the consecutive codes |int_val dotdot mu_val| and +|assign_int dotdot assign_mu_glue| correspond to each other nicely. + +@<Compute the register location |l| and its type |p|...@>= +{@+if (q!=internal_register) + {@+get_x_token(); + if ((cur_cmd >= assign_int)&&(cur_cmd <= assign_mu_glue)) + {@+l=cur_chr;p=cur_cmd-assign_int;goto found; + } + if (cur_cmd!=internal_register) + {@+print_err("You can't use `");print_cmd_chr(cur_cmd, cur_chr); +@.You can't use x after ...@> + print("' after ");print_cmd_chr(q, 0); + help1("I'm forgetting what you said and not changing anything."); + error();return; + } + } +if ((cur_chr < mem_bot)||(cur_chr > lo_mem_stat_max)) + {@+l=cur_chr;p=sa_type(l);e=true; + } +else{@+p=cur_chr-mem_bot;scan_register_num(); + if (cur_val > 255) + {@+find_sa_element(p, cur_val, true);l=cur_ptr;e=true; + } + else +switch (p) { +case int_val: l=cur_val+count_base;@+break; +case dimen_val: l=cur_val+scaled_base;@+break; +case glue_val: l=cur_val+skip_base;@+break; +case mu_val: l=cur_val+mu_skip_base; +} /*there are no other cases*/ + } +} +found: if (p < glue_val) @+if (e) w=sa_int(l);@+else w=eqtb[l].i; +else if (e) s=sa_ptr(l);@+else s=equiv(l) + +@ @<Compute result of |register| or |advance|...@>= +if (p < glue_val) + {@+if (p==int_val) scan_int();@+else scan_normal_dimen; + if (q==advance) cur_val=cur_val+w; + } +else{@+scan_glue(p); + if (q==advance) @<Compute the sum of two glue specs@>; + } + +@ @<Compute the sum of two glue specs@>= +{@+q=new_spec(cur_val);r=s; +delete_glue_ref(cur_val); +width(q)=width(q)+width(r); +if (stretch(q)==0) stretch_order(q)=normal; +if (stretch_order(q)==stretch_order(r)) stretch(q)=stretch(q)+stretch(r); +else if ((stretch_order(q) < stretch_order(r))&&(stretch(r)!=0)) + {@+stretch(q)=stretch(r);stretch_order(q)=stretch_order(r); + } +if (shrink(q)==0) shrink_order(q)=normal; +if (shrink_order(q)==shrink_order(r)) shrink(q)=shrink(q)+shrink(r); +else if ((shrink_order(q) < shrink_order(r))&&(shrink(r)!=0)) + {@+shrink(q)=shrink(r);shrink_order(q)=shrink_order(r); + } +cur_val=q; +} + +@ @<Compute result of |multiply| or |divide|...@>= +{@+scan_int(); +if (p < glue_val) + if (q==multiply) + if (p==int_val) cur_val=mult_integers(w, cur_val); + else cur_val=nx_plus_y(w, cur_val, 0); + else cur_val=x_over_n(w, cur_val); +else{@+r=new_spec(s); + if (q==multiply) + {@+width(r)=nx_plus_y(width(s), cur_val, 0); + stretch(r)=nx_plus_y(stretch(s), cur_val, 0); + shrink(r)=nx_plus_y(shrink(s), cur_val, 0); + } + else{@+width(r)=x_over_n(width(s), cur_val); + stretch(r)=x_over_n(stretch(s), cur_val); + shrink(r)=x_over_n(shrink(s), cur_val); + } + cur_val=r; + } +} + +@ The processing of boxes is somewhat different, because we may need +to scan and create an entire box before we actually change the value of the old +one. + +@<Assignments@>= +case set_box: {@+scan_register_num(); + if (global) n=global_box_flag+cur_val;@+else n=box_flag+cur_val; + scan_optional_equals(); + if (set_box_allowed) scan_box(n); + else{@+print_err("Improper ");print_esc("setbox"); +@.Improper \\setbox@> + help2("Sorry, \\setbox is not allowed after \\halign in a display,",@/ + "or between \\accent and an accented character.");error(); + } + } @+break; + +@ The |space_factor| or |prev_depth| settings are changed when a |set_aux| +command is sensed. Similarly, |prev_graf| is changed in the presence of +|set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of +|set_page_int|. These definitions are always global. + +When some dimension of a box register is changed, the change isn't exactly +global; but \TeX\ does not look at the \.{\\global} switch. + +@<Assignments@>= +case set_aux: alter_aux();@+break; +case set_prev_graf: alter_prev_graf();@+break; +case set_page_dimen: alter_page_so_far();@+break; +case set_page_int: alter_integer();@+break; +case set_box_dimen: alter_box_dimen();@+break; + +@ @<Declare subprocedures for |prefixed_command|@>= +static void alter_aux(void) +{@+halfword c; /*|hmode| or |vmode|*/ +if (cur_chr!=abs(mode)) report_illegal_case(); +else{@+c=cur_chr;scan_optional_equals(); + if (c==vmode) + {@+scan_normal_dimen;prev_depth=cur_val; + } + else{@+scan_int(); + if ((cur_val <= 0)||(cur_val > 32767)) + {@+print_err("Bad space factor"); +@.Bad space factor@> + help1("I allow only values in the range 1..32767 here."); + int_error(cur_val); + } + else space_factor=cur_val; + } + } +} + +@ @<Declare subprocedures for |prefixed_command|@>= +static void alter_prev_graf(void) +{@+int p; /*index into |nest|*/ +nest[nest_ptr]=cur_list;p=nest_ptr; +while (abs(nest[p].mode_field)!=vmode) decr(p); +scan_optional_equals();scan_int(); +if (cur_val < 0) + {@+print_err("Bad ");print_esc("prevgraf"); +@.Bad \\prevgraf@> + help1("I allow only nonnegative values here."); + int_error(cur_val); + } +else{@+nest[p].pg_field=cur_val;cur_list=nest[nest_ptr]; + } +} + +@ @<Declare subprocedures for |prefixed_command|@>= +static void alter_page_so_far(void) +{@+int c; /*index into |page_so_far|*/ +c=cur_chr;scan_optional_equals();scan_normal_dimen; +page_so_far[c]=cur_val; +} + +@ @<Declare subprocedures for |prefixed_command|@>= +static void alter_integer(void) +{@+small_number c; + /*0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc.*/ +c=cur_chr;scan_optional_equals();scan_int(); +if (c==0) dead_cycles=cur_val +@/@<Cases for |alter_integer|@>@;@/ +else insert_penalties=cur_val; +} + +@ @<Declare subprocedures for |prefixed_command|@>= +static void alter_box_dimen(void) +{@+small_number c; /*|width_offset| or |height_offset| or |depth_offset|*/ +pointer @!b; /*box register*/ +c=cur_chr;scan_register_num();fetch_box(b);scan_optional_equals(); +scan_normal_dimen; +if (b!=null) mem[b+c].sc=cur_val; +} + +@ Paragraph shapes are set up in the obvious way. + +@<Assignments@>= +case set_shape: {@+q=cur_chr;scan_optional_equals();scan_int();n=cur_val; + if (n <= 0) p=null; + else if (q > par_shape_loc) + {@+n=(cur_val/2)+1;p=get_node(2*n+1);info(p)=n; + n=cur_val;mem[p+1].i=n; /*number of penalties*/ + for (j=p+2; j<=p+n+1; j++) + {@+scan_int();mem[j].i=cur_val; /*penalty values*/ + } + if (!odd(n)) mem[p+n+2].i=0; /*unused*/ + } + else{@+p=get_node(2*n+1);info(p)=n; + for (j=1; j<=n; j++) + {@+scan_normal_dimen; + mem[p+2*j-1].sc=cur_val; /*indentation*/ + scan_normal_dimen; + mem[p+2*j].sc=cur_val; /*width*/ + } + } + define(q, shape_ref, p); + } @+break; + +@ Here's something that isn't quite so obvious. It guarantees that +|info(par_shape_ptr)| can hold any positive~|n| for which |get_node(2*n+1)| +doesn't overflow the memory capacity. + +@<Check the ``constant''...@>= +if (2*max_halfword < mem_top-mem_min) bad=41; + +@ New hyphenation data is loaded by the |hyph_data| command. + +@<Put each...@>= +primitive("hyphenation", hyph_data, 0); +@!@:hyphenation\_}{\.{\\hyphenation} primitive@> +primitive("patterns", hyph_data, 1); +@!@:patterns\_}{\.{\\patterns} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case hyph_data: if (chr_code==1) print_esc("patterns"); + else print_esc("hyphenation");@+break; + +@ @<Assignments@>= +case hyph_data: if (cur_chr==1) + { +#ifdef @!INIT +new_patterns();goto done;@; +#endif + print_err("Patterns can be loaded only by INITEX"); +@.Patterns can be...@> + help0;error(); + @/do@+{get_token();}@+ while (!(cur_cmd==right_brace)); /*flush the patterns*/ + return; + } + else{@+new_hyph_exceptions();goto done; + } @+break; + +@ All of \TeX's parameters are kept in |eqtb| except the font information, +the interaction mode, and the hyphenation tables; these are strictly global. + +@<Assignments@>= +case assign_font_dimen: {@+find_font_dimen(true);k=cur_val; + scan_optional_equals();scan_normal_dimen;font_info[k].sc=cur_val; + } @+break; +case assign_font_int: {@+n=cur_chr;scan_font_ident();f=cur_val; + scan_optional_equals();scan_int(); + if (n==0) hyphen_char[f]=cur_val;@+else skew_char[f]=cur_val; + } @+break; + +@ @<Put each...@>= +primitive("hyphenchar", assign_font_int, 0); +@!@:hyphen\_char\_}{\.{\\hyphenchar} primitive@> +primitive("skewchar", assign_font_int, 1); +@!@:skew\_char\_}{\.{\\skewchar} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case assign_font_int: if (chr_code==0) print_esc("hyphenchar"); + else print_esc("skewchar");@+break; + +@ Here is where the information for a new font gets loaded. + +@<Assignments@>= +case def_font: new_font(a);@+break; + +@ @<Declare subprocedures for |prefixed_command|@>= +static void new_font(small_number @!a) +{@+ +pointer u; /*user's font identifier*/ +scaled @!s; /*stated ``at'' size, or negative of scaled magnification*/ +int @!f; /*runs through existing fonts*/ +str_number @!t; /*name for the frozen font identifier*/ +int @!old_setting; /*holds |selector| setting*/ +str_number @!flushable_string; /*string not yet referenced*/ +if (job_name==0) open_log_file(); + /*avoid confusing \.{texput} with the font name*/ +@.texput@> +get_r_token();u=cur_cs; +if (u >= hash_base) t=text(u); +else if (u >= single_base) + if (u==null_cs) t=s_no("FONT");@+else t=u-single_base; +else{@+old_setting=selector;selector=new_string; + print("FONT");printn(u-active_base);selector=old_setting; +@.FONTx@> + str_room(1);t=make_string(); + } +define(u, set_font, null_font);scan_optional_equals();scan_file_name(); +@<Scan the font size specification@>; +@<If this font has already been loaded, set |f| to the internal font number +and |goto common_ending|@>; +f=read_font_info(u, cur_name, cur_area, s); +common_ending: define(u, set_font, f);eqtb[font_id_base+f]=eqtb[u];font_id_text(f)=t; +} + +@ @<Scan the font size specification@>= +name_in_progress=true; /*this keeps |cur_name| from being changed*/ +if (scan_keyword("at")) @<Put the \(p)(positive) `at' size into |s|@>@; +@.at@> +else if (scan_keyword("scaled")) +@.scaled@> + {@+scan_int();s=-cur_val; + if ((cur_val <= 0)||(cur_val > 32768)) + {@+print_err("Illegal magnification has been changed to 1000");@/ +@.Illegal magnification...@> + help1("The magnification ratio must be between 1 and 32768."); + int_error(cur_val);s=-1000; + } + } +else s=-1000; +name_in_progress=false + +@ @<Put the \(p)(positive) `at' size into |s|@>= +{@+scan_normal_dimen;s=cur_val; +if ((s <= 0)||(s >= 01000000000)) + {@+print_err("Improper `at' size ("); + print_scaled(s);print("pt), replaced by 10pt"); +@.Improper `at' size...@> + help2("I can only handle fonts at positive sizes that are",@/ + "less than 2048pt, so I've changed what you said to 10pt."); + error();s=10*unity; + } +} + +@ When the user gives a new identifier to a font that was previously loaded, +the new name becomes the font identifier of record. Font names `\.{xyz}' and +`\.{XYZ}' are considered to be different. + +@<If this font has already been loaded...@>= +flushable_string=str_ptr-1; +for (f=font_base+1; f<=font_ptr; f++) + if (str_eq_str(font_name[f], cur_name)&&str_eq_str(font_area[f], cur_area)) + {@+if (cur_name==flushable_string) + {@+flush_string;cur_name=font_name[f]; + } + if (s > 0) + {@+if (s==font_size[f]) goto common_ending; + } + else if (font_size[f]==xn_over_d(font_dsize[f],-s, 1000)) + goto common_ending; + } + +@ @<Cases of |print_cmd_chr|...@>= +case set_font: {@+print("select font ");slow_print(font_name[chr_code]); + if (font_size[chr_code]!=font_dsize[chr_code]) + {@+print(" at ");print_scaled(font_size[chr_code]); + print("pt"); + } + } @+break; + +@ @<Put each...@>= +primitive("batchmode", set_interaction, batch_mode); +@!@:batch\_mode\_}{\.{\\batchmode} primitive@> +primitive("nonstopmode", set_interaction, nonstop_mode); +@!@:nonstop\_mode\_}{\.{\\nonstopmode} primitive@> +primitive("scrollmode", set_interaction, scroll_mode); +@!@:scroll\_mode\_}{\.{\\scrollmode} primitive@> +primitive("errorstopmode", set_interaction, error_stop_mode); +@!@:error\_stop\_mode\_}{\.{\\errorstopmode} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case set_interaction: switch (chr_code) { + case batch_mode: print_esc("batchmode");@+break; + case nonstop_mode: print_esc("nonstopmode");@+break; + case scroll_mode: print_esc("scrollmode");@+break; + default:print_esc("errorstopmode"); + } @+break; + +@ @<Assignments@>= +case set_interaction: new_interaction();@+break; + +@ @<Declare subprocedures for |prefixed_command|@>= +static void new_interaction(void) +{@+print_ln(); +interaction=cur_chr; +@<Initialize the print |selector| based on |interaction|@>; +if (log_opened) selector=selector+2; +} + +@ The \.{\\afterassignment} command puts a token into the global +variable |after_token|. This global variable is examined just after +every assignment has been performed. + +@<Glob...@>= +static halfword @!after_token; /*zero, or a saved token*/ + +@ @<Set init...@>= +after_token=0; + +@ @<Cases of |main_control| that don't...@>= +any_mode(after_assignment): {@+get_token();after_token=cur_tok; + } @+break; + +@ @<Insert a token saved by \.{\\afterassignment}, if any@>= +if (after_token!=0) + {@+cur_tok=after_token;back_input();after_token=0; + } + +@ Here is a procedure that might be called `Get the next non-blank non-relax +non-call non-assignment token'. + +@<Declare act...@>= +static void do_assignments(void) +{@+ +loop{@+@<Get the next non-blank non-relax...@>; + if (cur_cmd <= max_non_prefixed_command) return; + set_box_allowed=false;prefixed_command();set_box_allowed=true; + } +} + +@ @<Cases of |main_control| that don't...@>= +any_mode(after_group): {@+get_token();save_for_after(cur_tok); + } @+break; + +@ Files for \.{\\read} are opened and closed by the |in_stream| command. + +@<Put each...@>= +primitive("openin", in_stream, 1); +@!@:open\_in\_}{\.{\\openin} primitive@> +primitive("closein", in_stream, 0); +@!@:close\_in\_}{\.{\\closein} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case in_stream: if (chr_code==0) print_esc("closein"); + else print_esc("openin");@+break; + +@ @<Cases of |main_control| that don't...@>= +any_mode(in_stream): open_or_close_in();@+break; + +@ @<Declare act...@>= +static void open_or_close_in(void) +{@+int c; /*1 for \.{\\openin}, 0 for \.{\\closein}*/ +int @!n; /*stream number*/ +c=cur_chr;scan_four_bit_int();n=cur_val; +if (read_open[n]!=closed) + {@+a_close(&read_file[n]);read_open[n]=closed; + } +if (c!=0) + {@+scan_optional_equals();scan_file_name(); + pack_cur_name(".tex"); + if (a_open_in(&read_file[n])) + { read_open[n]=just_open; + @<Set new |read_file_num[n]|@>@; + } + } +} + +@ The user can issue messages to the terminal, regardless of the +current mode. + +@<Cases of |main_control| that don't...@>= +any_mode(message): issue_message();@+break; + +@ @<Put each...@>= +primitive("message", message, 0); +@!@:message\_}{\.{\\message} primitive@> +primitive("errmessage", message, 1); +@!@:err\_message\_}{\.{\\errmessage} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case message: if (chr_code==0) print_esc("message"); + else print_esc("errmessage");@+break; + +@ @<Declare act...@>= +static void issue_message(void) +{@+int old_setting; /*holds |selector| setting*/ +int @!c; /*identifies \.{\\message} and \.{\\errmessage}*/ +str_number @!s; /*the message*/ +c=cur_chr;link(garbage)=scan_toks(false, true); +old_setting=selector;selector=new_string; +token_show(def_ref);selector=old_setting; +flush_list(def_ref); +str_room(1);s=make_string(); +if (c==0) @<Print string |s| on the terminal@>@; +else@<Print string |s| as an error message@>; +flush_string; +} + +@ @<Print string |s| on the terminal@>= +{@+if (term_offset+length(s) > max_print_line-2) print_ln(); +else if ((term_offset > 0)||(file_offset > 0)) print_char(' '); +slow_print(s);update_terminal; +} + +@ 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...@>= +static bool @!long_help_seen; /*has the long \.{\\errmessage} help been used?*/ + +@ @<Set init...@>=long_help_seen=false; + +@ @<Print string |s| as an error message@>= +{@+print_err("");slow_print(s); +if (err_help!=null) use_err_help=true; +else if (long_help_seen) help1("(That was another \\errmessage.)")@; +else{@+if (interaction < error_stop_mode) long_help_seen=true; + help4("This error message was generated by an \\errmessage",@/ + "command, so I can't give any explicit help.",@/ + "Pretend that you're Hercule Poirot: Examine all clues,",@/ +@^Poirot, Hercule@> + "and deduce the truth by order and method."); + } +error();use_err_help=false; +} + +@ The |error| routine calls on |give_err_help| if help is requested from +the |err_help| parameter. + +@p static void give_err_help(void) +{@+token_show(err_help); +} + +@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by +building a token list and then changing the cases of the letters in it. + +@<Cases of |main_control| that don't...@>= +any_mode(case_shift): shift_case();@+break; + +@ @<Put each...@>= +primitive("lowercase", case_shift, lc_code_base); +@!@:lowercase\_}{\.{\\lowercase} primitive@> +primitive("uppercase", case_shift, uc_code_base); +@!@:uppercase\_}{\.{\\uppercase} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case case_shift: if (chr_code==lc_code_base) print_esc("lowercase"); + else print_esc("uppercase");@+break; + +@ @<Declare act...@>= +static void shift_case(void) +{@+pointer b; /*|lc_code_base| or |uc_code_base|*/ +pointer @!p; /*runs through the token list*/ +halfword @!t; /*token*/ +eight_bits @!c; /*character code*/ +b=cur_chr;p=scan_toks(false, false);p=link(def_ref); +while (p!=null) + {@+@<Change the case of the token in |p|, if a change is appropriate@>; + p=link(p); + } +back_list(link(def_ref));free_avail(def_ref); /*omit reference count*/ +} + +@ When the case of a |chr_code| changes, we don't change the |cmd|. +We also change active characters, using the fact that +|cs_token_flag+active_base| is a multiple of~256. +@^data structure assumptions@> + +@<Change the case of the token in |p|, if a change is appropriate@>= +t=info(p); +if (t < cs_token_flag+single_base) + {@+c=t%256; + if (equiv(b+c)!=0) info(p)=t-c+equiv(b+c); + } + +@ We come finally to the last pieces missing from |main_control|, namely the +`\.{\\show}' commands that are useful when debugging. + +@<Cases of |main_control| that don't...@>= +any_mode(xray): show_whatever();@+break; + +@ @d show_code 0 /* \.{\\show} */ +@d show_box_code 1 /* \.{\\showbox} */ +@d show_the_code 2 /* \.{\\showthe} */ +@d show_lists_code 3 /* \.{\\showlists} */ + +@<Put each...@>= +primitive("show", xray, show_code); +@!@:show\_}{\.{\\show} primitive@> +primitive("showbox", xray, show_box_code); +@!@:show\_box\_}{\.{\\showbox} primitive@> +primitive("showthe", xray, show_the_code); +@!@:show\_the\_}{\.{\\showthe} primitive@> +primitive("showlists", xray, show_lists_code); +@!@:show\_lists\_code\_}{\.{\\showlists} primitive@> + +@ @<Cases of |print_cmd_chr|...@>= +case xray: switch (chr_code) { + case show_box_code: print_esc("showbox");@+break; + case show_the_code: print_esc("showthe");@+break; + case show_lists_code: print_esc("showlists");@+break; + @<Cases of |xray| for |print_cmd_chr|@>@;@/ + default:print_esc("show"); + } @+break; + +@ @<Declare act...@>= +static void show_whatever(void) +{@+ +pointer p; /*tail of a token list to show*/ +small_number @!t; /*type of conditional being shown*/ +int @!m; /*upper bound on |fi_or_else| codes*/ +int @!l; /*line where that conditional began*/ +int @!n; /*level of \.{\\if...\\fi} nesting*/ +switch (cur_chr) { +case show_lists_code: {@+begin_diagnostic();show_activities(); + } @+break; +case show_box_code: @<Show the current contents of a box@>@;@+break; +case show_code: @<Show the current meaning of a token, then |goto common_ending|@>@; +@<Cases for |show_whatever|@>@;@/ +default:@<Show the current value of some parameter or register, then |goto +common_ending|@>@; +} @/ +@<Complete a potentially long \.{\\show} command@>; +common_ending: if (interaction < error_stop_mode) + {@+help0;decr(error_count); + } +else if (tracing_online > 0) + {@+@t@>@;@/ + help3("This isn't an error message; I'm just \\showing something.",@/ + "Type `I\\show...' to show more (e.g., \\show\\cs,",@/ + "\\showthe\\count10, \\showbox255, \\showlists)."); + } +else{@+@t@>@;@/ + help5("This isn't an error message; I'm just \\showing something.",@/ + "Type `I\\show...' to show more (e.g., \\show\\cs,",@/ + "\\showthe\\count10, \\showbox255, \\showlists).",@/ + "And type `I\\tracingonline=1\\show...' to show boxes and",@/ + "lists on your terminal as well as in the transcript file."); + } +error(); +} + +@ @<Show the current meaning of a token...@>= +{@+get_token(); +if (interaction==error_stop_mode) wake_up_terminal; +print_nl("> "); +if (cur_cs!=0) + {@+sprint_cs(cur_cs);print_char('='); + } +print_meaning();goto common_ending; +} + +@ @<Cases of |print_cmd_chr|...@>= +case undefined_cs: print("undefined");@+break; +case call: case long_call: case outer_call: + case long_outer_call: {@+n=cmd-call; + if (info(link(chr_code))==protected_token) n=n+4; + if (odd(n/4)) print_esc("protected"); + if (odd(n)) print_esc("long"); + if (odd(n/2)) print_esc("outer"); + if (n > 0) print_char(' '); + print("macro"); + } @+break; +case end_template: print_esc("outer endtemplate");@+break; + +@ @<Show the current contents of a box@>= +{@+scan_register_num();fetch_box(p);begin_diagnostic(); +print_nl("> \\box");print_int(cur_val);print_char('='); +if (p==null) print("void");@+else show_box(p); +} + +@ @<Show the current value of some parameter...@>= +{@+the_toks(); +if (interaction==error_stop_mode) wake_up_terminal; +print_nl("> ");token_show(temp_head); +flush_list(link(temp_head));goto common_ending; +} + +@ @<Complete a potentially long \.{\\show} command@>= +end_diagnostic(true);print_err("OK"); +@.OK@> +if (selector==term_and_log) if (tracing_online <= 0) + {@+selector=term_only;print(" (see the transcript file)"); + selector=term_and_log; + } + +@* Dumping and undumping the tables. +After \.{INITEX} has seen a collection of fonts and macros, it +can write all the necessary information on an auxiliary file so +that production versions of \TeX\ are able to initialize their +memory at high speed. The present section of the program takes +care of such output and input. We shall consider simultaneously +the processes of storing and restoring, +so that the inverse relation between them is clear. +@.INITEX@> + +The global variable |format_ident| is a string that is printed right +after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this +string says simply `\.{ (INITEX)}'; for other versions of \TeX\ it says, +for example, `\.{ (preloaded format=plain 1982.11.19)}', showing the year, +month, and day that the format file was created. We have |format_ident==0| +before \TeX's tables are loaded. + +@<Glob...@>= +static str_number @!format_ident, frozen_format_ident; + +@ @<Set init...@>= +format_ident=frozen_format_ident=0; + +@ We keep a copy of the initial value, be able to test for it later. + +@<Initialize table entries...@>= +format_ident=frozen_format_ident=s_no(" (INITEX)"); + +@ @<Declare act...@>= +#ifdef @!INIT +static void store_fmt_file(void) +{@+ +int j, @!k, @!l; /*all-purpose indices*/ +int @!p, @!q; /*all-purpose pointers*/ +int @!x; /*something to dump*/ +four_quarters @!w; /*four ASCII codes*/ +@<If dumping is not allowed, abort@>; +@<Create the |format_ident|, open the format file, and inform the user that +dumping has begun@>; +@<Dump constants for consistency check@>; +@<Dump the string pool@>; +@<Dump the dynamic memory@>; +@<Dump the table of equivalents@>; +@<Dump the font information@>; +@<Dump the hyphenation tables@>; +@<Dump the full file names@>; +@<Dump the file and line information@>; +@<Dump a couple more things and the closing check word@>; +@<Close the format file@>; +} +#endif + +@ Corresponding to the procedure that dumps a format file, we have a function +that reads one in. The function returns |false| if the dumped format is +incompatible with the present \TeX\ table sizes, etc. + +@d too_small(X) {@+wake_up_terminal; + wterm_ln("---! Must increase the %s", X); +@.Must increase the x@> + goto bad_fmt; + } + +@p @t\4@>@<Declare the function called |open_fmt_file|@>@; +static bool load_fmt_file(void) +{@+ +int j, @!k; /*all-purpose indices*/ +int @!p, @!q; /*all-purpose pointers*/ +int @!x; /*something undumped*/ +four_quarters @!w; /*four ASCII codes*/ +@<Undump constants for consistency check@>; +@<Undump the string pool@>; +@<Undump the dynamic memory@>; +@<Undump the table of equivalents@>; +@<Undump the font information@>; +@<Undump the hyphenation tables@>; +@<Undump the full file names@>; +@<Undump the file and line information@>; +@<Undump a couple more things and the closing check word@>; +return true; /*it worked!*/ +bad_fmt: wake_up_terminal; + wterm_ln("(Fatal format file error; I'm stymied)"); +@.Fatal format file error@> +return false; +} + +@ The user is not allowed to dump a format file unless |save_ptr==0|. +This condition implies that |cur_level==level_one|, hence +the |xeq_level| array is constant and it need not be dumped. + +@<If dumping is not allowed, abort@>= +if (save_ptr!=0) + {@+print_err("You can't dump inside a group"); +@.You can't dump...@> + help1("`{...\\dump}' is a no-no.");succumb; + } + +@ Format files consist of |memory_word| items, and we use the following +macros to dump words of different types: + +@d dump_wd(A) {@+fmt_file.d=A;put(fmt_file);@+} +@d dump_int(A) {@+fmt_file.d.i=A;put(fmt_file);@+} +@d dump_hh(A) {@+fmt_file.d.hh=A;put(fmt_file);@+} +@d dump_qqqq(A) {@+fmt_file.d.qqqq=A;put(fmt_file);@+} + +@<Glob...@>= +static word_file @!fmt_file; /*for input or output of format information*/ + +@ The inverse macros are slightly more complicated, since we need to check +the range of the values we are reading in. We say `|undump(a)(b)(x)|' to +read an integer value |x| that is supposed to be in the range |a <= x <= b|. +System error messages should be suppressed when undumping. +@^system dependencies@> + +@d undump_wd(A) {@+get(fmt_file);A=fmt_file.d;@+} +@d undump_int(A) {@+get(fmt_file);A=fmt_file.d.i;@+} +@d undump_hh(A) {@+get(fmt_file);A=fmt_file.d.hh;@+} +@d undump_qqqq(A) {@+get(fmt_file);A=fmt_file.d.qqqq;@+} +@d undump(A, B, C) {@+undump_int(x);if ((x < A)||(x > B)) goto bad_fmt;@+else C=x;@+} +@d undump_size(A, B, C, D) {@+undump_int(x); + if (x < A) goto bad_fmt;if (x > B) too_small(C)@;@+else D=x;@+} + +@ 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(0);@/ +@<Dump the \eTeX\ state@>@/ +@<Dump the \Prote\ state@>@/ +@<Dump the |ROM| array@>@/ +dump_int(mem_bot);@/ +dump_int(mem_top);@/ +dump_int(eqtb_size);@/ +dump_int(hash_prime);@/ +dump_int(hyph_size) + +@ Sections of a \.{WEB} program that are ``commented out'' still contribute +strings to the string pool; therefore \.{INITEX} and \TeX\ will have +the same strings. (And it is, of course, a good thing that they do.) +@.WEB@> +@^string pool@> + +@<Undump constants for consistency check@>= +x=fmt_file.d.i; +if (x!=0) goto bad_fmt; /*check that strings are the same*/ +@/@<Undump the \eTeX\ state@>@/ +@/@<Undump the \Prote\ state@>@/ +@/@<Undump the |ROM| array@>@/ +undump_int(x); +if (x!=mem_bot) goto bad_fmt; +undump_int(x); +if (x!=mem_top) goto bad_fmt; +undump_int(x); +if (x!=eqtb_size) goto bad_fmt; +undump_int(x); +if (x!=hash_prime) goto bad_fmt; +undump_int(x); +if (x!=hyph_size) goto bad_fmt + +@ @d dump_four_ASCII + w.b0=qi(so(str_pool[k]));w.b1=qi(so(str_pool[k+1])); + w.b2=qi(so(str_pool[k+2]));w.b3=qi(so(str_pool[k+3])); + dump_qqqq(w) + +@<Dump the string pool@>= +dump_int(pool_ptr); +dump_int(str_ptr); +for (k=0; k<=str_ptr; k++) dump_int(str_start[k]); +k=0; +while (k+4 < pool_ptr) + {@+dump_four_ASCII;k=k+4; + } +k=pool_ptr-4;dump_four_ASCII; +print_ln();print_int(str_ptr);print(" strings of total length "); +print_int(pool_ptr) + +@ @d undump_four_ASCII + undump_qqqq(w); + str_pool[k]=si(qo(w.b0));str_pool[k+1]=si(qo(w.b1)); + str_pool[k+2]=si(qo(w.b2));str_pool[k+3]=si(qo(w.b3)) + +@<Undump the string pool@>= +undump_size(0, pool_size,"string pool size", pool_ptr); +undump_size(0, max_strings,"max strings", str_ptr); +for (k=0; k<=str_ptr; k++) undump(0, pool_ptr, str_start[k]); +k=0; +while (k+4 < pool_ptr) + {@+undump_four_ASCII;k=k+4; + } +k=pool_ptr-4;undump_four_ASCII; +init_str_ptr=str_ptr;init_pool_ptr=pool_ptr + +@ By sorting the list of available spaces in the variable-size portion of +|mem|, we are usually able to get by without having to dump very much +of the dynamic memory. + +We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid +information even when it has not been gathering statistics. + +@<Dump the dynamic memory@>= +sort_avail();var_used=0; +dump_int(lo_mem_max);dump_int(rover); +if (eTeX_ex) for (k=int_val; k<=tok_val; k++) dump_int(sa_root[k]); +p=mem_bot;q=rover;x=0; +@/do@+{for (k=p; k<=q+1; k++) dump_wd(mem[k]); +x=x+q+2-p;var_used=var_used+q-p; +p=q+node_size(q);q=rlink(q); +}@+ while (!(q==rover)); +var_used=var_used+lo_mem_max-p;dyn_used=mem_end+1-hi_mem_min;@/ +for (k=p; k<=lo_mem_max; k++) dump_wd(mem[k]); +x=x+lo_mem_max+1-p; +dump_int(hi_mem_min);dump_int(avail); +for (k=hi_mem_min; k<=mem_end; k++) dump_wd(mem[k]); +p=avail; +while (p!=null) +{@+fl_mem[p]=0; /*clear unallocated |fl_mem|*/ + decr(dyn_used);p=link(p); +} +dump_int(var_used);dump_int(dyn_used); +print_ln();print_int(x);print_char('&');print_int(mem_end+1-hi_mem_min); +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); +if (eTeX_ex) for (k=int_val; k<=tok_val; k++) + undump(null, lo_mem_max, sa_root[k]); +p=mem_bot;q=rover; +@/do@+{for (k=p; k<=q+1; k++) undump_wd(mem[k]); +p=q+node_size(q); +if ((p > lo_mem_max)||((q >= rlink(q))&&(rlink(q)!=rover))) goto bad_fmt; +q=rlink(q); +}@+ while (!(q==rover)); +for (k=p; k<=lo_mem_max; k++) undump_wd(mem[k]); +if (mem_min < mem_bot-2) /*make more low memory available*/ + {@+p=llink(rover);q=mem_min+1; + link(mem_min)=null;info(mem_min)=null; /*we don't use the bottom word*/ + rlink(p)=q;llink(rover)=q;@/ + rlink(q)=rover;llink(q)=p;link(q)=empty_flag; + node_size(q)=mem_bot-q; + } +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; k<=mem_end; k++) undump_wd(mem[k]); +undump_int(var_used);undump_int(dyn_used) + +@ @<Dump the table of equivalents@>= +@<Dump regions 1 to 4 of |eqtb|@>; +@<Dump regions 5 and 6 of |eqtb|@>; +dump_int(par_loc);dump_int(write_loc);@/ +dump_int(input_loc);@/ +@<Dump the hash table@>@; + +@ @<Undump the table of equivalents@>= +@<Undump regions 1 to 6 of |eqtb|@>; +undump(hash_base, frozen_control_sequence, par_loc); +par_token=cs_token_flag+par_loc;@/ +undump(hash_base, frozen_control_sequence, write_loc);@/ +undump(hash_base, frozen_control_sequence, input_loc); +input_token=cs_token_flag+input_loc;@/ +@<Undump the hash table@>@; + +@ The table of equivalents usually contains repeated information, so we dump it +in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the +format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra +copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. + +@<Dump regions 1 to 4 of |eqtb|@>= +k=active_base; +@/do@+{j=k; +while (j < int_base-1) + {@+if ((equiv(j)==equiv(j+1))&&(eq_type(j)==eq_type(j+1))&&@| + (eq_level(j)==eq_level(j+1))) goto found1; + incr(j); + } +l=int_base;goto done1; /*|j==int_base-1|*/ +found1: incr(j);l=j; +while (j < int_base-1) + {@+if ((equiv(j)!=equiv(j+1))||(eq_type(j)!=eq_type(j+1))||@| + (eq_level(j)!=eq_level(j+1))) goto done1; + incr(j); + } +done1: dump_int(l-k); +while (k < l) + {@+dump_wd(eqtb[k]);incr(k); + } +k=j+1;dump_int(k-l); +}@+ while (!(k==int_base)) + +@ @<Dump regions 5 and 6 of |eqtb|@>= +@/do@+{j=k; +while (j < eqtb_size) + {@+if (eqtb[j].i==eqtb[j+1].i) goto found2; + incr(j); + } +l=eqtb_size+1;goto done2; /*|j==eqtb_size|*/ +found2: incr(j);l=j; +while (j < eqtb_size) + {@+if (eqtb[j].i!=eqtb[j+1].i) goto done2; + incr(j); + } +done2: dump_int(l-k); +while (k < l) + {@+dump_wd(eqtb[k]);incr(k); + } +k=j+1;dump_int(k-l); +}@+ while (!(k > eqtb_size)) + +@ @<Undump regions 1 to 6 of |eqtb|@>= +k=active_base; +@/do@+{undump_int(x); +if ((x < 1)||(k+x > eqtb_size+1)) goto bad_fmt; +for (j=k; j<=k+x-1; j++) undump_wd(eqtb[j]); +k=k+x; +undump_int(x); +if ((x < 0)||(k+x > eqtb_size+1)) goto bad_fmt; +for (j=k; j<=k+x-1; j++) eqtb[j]=eqtb[k-1]; +k=k+x; +}@+ while (!(k > eqtb_size)) + +@ A different scheme is used to compress the hash table, since its lower +region is usually sparse. When |text(p)!=0| for |p <= hash_used|, we output +two words, |p| and |hash[p]|. The hash table is, of course, densely packed +for |p >= hash_used|, so the remaining entries are output in a~block. + +@<Dump the hash table@>= +dump_int(hash_used);cs_count=frozen_control_sequence-1-hash_used; +for (p=hash_base; p<=hash_used; p++) if (text(p)!=0) + {@+dump_int(p);dump_hh(hash[p]);incr(cs_count); + } +for (p=hash_used+1; p<=undefined_control_sequence-1; p++) dump_hh(hash[p]); +dump_int(cs_count);@/ +print_ln();print_int(cs_count);print(" multiletter control sequences") + +@ @<Undump the hash table@>= +undump(hash_base, frozen_control_sequence, hash_used);p=hash_base-1; +@/do@+{undump(p+1, hash_used, p);undump_hh(hash[p]); +}@+ while (!(p==hash_used)); +for (p=hash_used+1; p<=undefined_control_sequence-1; p++) undump_hh(hash[p]); +undump_int(cs_count) + +@ @<Dump the font information@>= +dump_int(fmem_ptr); +for (k=0; k<=fmem_ptr-1; k++) dump_wd(font_info[k]); +dump_int(font_ptr); +for (k=null_font; k<=font_ptr; k++) + @<Dump the array info for internal font number |k|@>; +print_ln();print_int(fmem_ptr-7);print(" words of font info for "); +print_int(font_ptr-font_base);print(" preloaded font"); +if (font_ptr!=font_base+1) print_char('s') + +@ @<Undump the font information@>= +undump_size(7, font_mem_size,"font mem size", fmem_ptr); +for (k=0; k<=fmem_ptr-1; k++) undump_wd(font_info[k]); +undump_size(font_base, font_max,"font max", font_ptr); +for (k=null_font; k<=font_ptr; k++) + @<Undump the array info for internal font number |k|@>@; + +@ @<Dump the array info for internal font number |k|@>= +{@+dump_qqqq(font_check[k]); +dump_int(font_size[k]); +dump_int(font_dsize[k]); +dump_int(font_params[k]);@/ +dump_int(hyphen_char[k]); +dump_int(skew_char[k]);@/ +dump_int(font_name[k]); +dump_int(font_area[k]);@/ +dump_int(font_bc[k]); +dump_int(font_ec[k]);@/ +dump_int(char_base[k]); +dump_int(width_base[k]); +dump_int(height_base[k]);@/ +dump_int(depth_base[k]); +dump_int(italic_base[k]); +dump_int(lig_kern_base[k]);@/ +dump_int(kern_base[k]); +dump_int(exten_base[k]); +dump_int(param_base[k]);@/ +dump_int(font_glue[k]);@/ +dump_int(bchar_label[k]); +dump_int(font_bchar[k]); +dump_int(font_false_bchar[k]);@/ +print_nl("\\font");printn_esc(font_id_text(k));print_char('='); +print_file_name(font_name[k], font_area[k],empty_string); +if (font_size[k]!=font_dsize[k]) + {@+print(" at ");print_scaled(font_size[k]);print("pt"); + } +} + +@ @<Undump the array info for internal font number |k|@>= +{@+undump_qqqq(font_check[k]);@/ +undump_int(font_size[k]); +undump_int(font_dsize[k]); +undump(min_halfword, max_halfword, font_params[k]);@/ +undump_int(hyphen_char[k]); +undump_int(skew_char[k]);@/ +undump(0, str_ptr, font_name[k]); +undump(0, str_ptr, font_area[k]);@/ +undump(0, 255, font_bc[k]); +undump(0, 255, font_ec[k]);@/ +undump_int(char_base[k]); +undump_int(width_base[k]); +undump_int(height_base[k]);@/ +undump_int(depth_base[k]); +undump_int(italic_base[k]); +undump_int(lig_kern_base[k]);@/ +undump_int(kern_base[k]); +undump_int(exten_base[k]); +undump_int(param_base[k]);@/ +undump(min_halfword, lo_mem_max, font_glue[k]);@/ +undump(0, fmem_ptr-1, bchar_label[k]); +undump(min_quarterword, non_char, font_bchar[k]); +undump(min_quarterword, non_char, font_false_bchar[k]); +} + +@ @<Dump the hyphenation tables@>= +dump_int(hyph_count); +for (k=0; k<=hyph_size; k++) if (hyph_word[k]!=0) + {@+dump_int(k);dump_int(hyph_word[k]);dump_int(hyph_list[k]); + } +print_ln();print_int(hyph_count);print(" hyphenation exception"); +if (hyph_count!=1) print_char('s'); +if (trie_not_ready) init_trie(); +dump_int(trie_max); +dump_int(hyph_start); +for (k=0; k<=trie_max; k++) dump_hh(trie[k]); +dump_int(trie_op_ptr); +for (k=1; k<=trie_op_ptr; k++) + {@+dump_int(hyf_distance[k]); + dump_int(hyf_num[k]); + dump_int(hyf_next[k]); + } +print_nl("Hyphenation trie of length ");print_int(trie_max); +@.Hyphenation trie...@> +print(" has ");print_int(trie_op_ptr);print(" op"); +if (trie_op_ptr!=1) print_char('s'); +print(" out of ");print_int(trie_op_size); +for (k=255; k>=0; k--) if (trie_used[k] > min_quarterword) + {@+print_nl(" ");print_int(qo(trie_used[k])); + print(" for language ");print_int(k); + dump_int(k);dump_int(qo(trie_used[k])); + } + +@ Only ``nonempty'' parts of |op_start| need to be restored. + +@<Undump the hyphenation tables@>= +undump(0, hyph_size, hyph_count); +for (k=1; k<=hyph_count; k++) + {@+undump(0, hyph_size, j); + undump(0, str_ptr, hyph_word[j]); + undump(min_halfword, max_halfword, hyph_list[j]); + } +undump_size(0, trie_size,"trie size", j); +#ifdef @!INIT +trie_max=j; +#endif +undump(0, j, hyph_start); +for (k=0; k<=j; k++) undump_hh(trie[k]); +undump_size(0, trie_op_size,"trie op size", j); +#ifdef @!INIT +trie_op_ptr=j; +#endif +for (k=1; k<=j; k++) + {@+undump(0, 63, hyf_distance[k]); /*a |small_number|*/ + undump(0, 63, hyf_num[k]); + undump(min_quarterword, max_quarterword, hyf_next[k]); + } +#ifdef @!INIT +for (k=0; k<=255; k++) trie_used[k]=min_quarterword; +#endif +@;@/ +k=256; +while (j > 0) + {@+undump(0, k-1, k);undump(1, j, x); +#ifdef @!INIT +trie_used[k]=qi(x); +#endif +@;@/ + j=j-x;op_start[k]=qo(j); + } +#ifdef @!INIT +trie_not_ready=false +#endif + +@ We have already printed a lot of statistics, so we set |tracing_stats=0| +to prevent them from appearing again. + +@<Dump a couple more things and the closing check word@>= +dump_int(interaction);dump_int(format_ident);dump_int(69069); +tracing_stats=0 + +@ @<Undump a couple more things and the closing check word@>= +undump(batch_mode, error_stop_mode, interaction); +if (interaction_option>=0) interaction=interaction_option; /* \TeX\ Live */ +undump(0, str_ptr, format_ident); +undump_int(x); +if ((x!=69069)||eof(fmt_file)) goto bad_fmt + +@ @<Create the |format_ident|...@>= +selector=new_string; +print(" (preloaded format=");printn(job_name);print_char(' '); +print_int(year);print_char('.'); +print_int(month);print_char('.');print_int(day);print_char(')'); +if (interaction==batch_mode) selector=log_only; +else selector=term_and_log; +str_room(1); +format_ident=make_string(); +pack_job_name(format_extension); +while (!w_open_out(&fmt_file)) + prompt_file_name("format file name", format_extension); +print_nl("Beginning to dump on file "); +@.Beginning to dump...@> +slow_print(w_make_name_string(&fmt_file));flush_string; +print_nl("");slow_print(format_ident) + +@ @<Close the format file@>= +w_close(&fmt_file) + +@* The main program. +This is it: the part of \TeX\ that executes all those procedures we have +written. + +Well---almost. Let's leave space for a few more routines that we may +have forgotten. + +@p @<Last-minute procedures@>@; + +@ We have noted that there are two versions of \TeX82. One, called \.{INITEX}, +@.INITEX@> +has to be run first; it initializes everything from scratch, without +reading a format file, and it has the capability of dumping a format file. +The other one is called `\.{VIRTEX}'; it is a ``virgin'' program that needs +@.VIRTEX@> +to input a format file in order to get started. \.{VIRTEX} typically has +more memory capacity than \.{INITEX}, because it does not need the space +consumed by the auxiliary hyphenation tables and the numerous calls on +|primitive|, etc. + +The \.{VIRTEX} program cannot read a format file instantaneously, of course; +the best implementations therefore allow for production versions of \TeX\ that +not only avoid the loading routine for \PASCAL\ object code, they also have +a format file pre-loaded. This is impossible to do if we stick to standard +\PASCAL; but there is a simple way to fool many systems into avoiding the +initialization, as follows:\quad(1)~We declare a global integer variable +called |ready_already|. The probability is negligible that this +variable holds any particular value like 314159 when \.{VIRTEX} is first +loaded.\quad(2)~After we have read in a format file and initialized +everything, we set |ready_already=314159|.\quad(3)~Soon \.{VIRTEX} +will print `\.*', waiting for more input; and at this point we +interrupt the program and save its core image in some form that the +operating system can reload speedily.\quad(4)~When that core image is +activated, the program starts again at the beginning; but now +|ready_already==314159| and all the other global variables have +their initial values too. The former chastity has vanished! + +In other words, if we allow ourselves to test the condition +|ready_already==314159|, before |ready_already| has been +assigned a value, we can avoid the lengthy initialization. Dirty tricks +rarely pay off so handsomely. +@^dirty \PASCAL@> +@^system dependencies@> + +On systems that allow such preloading, the standard program called \.{TeX} +should be the one that has \.{plain} format preloaded, since that agrees +with {\sl The \TeX book}. Other versions, e.g., \.{AmSTeX}, should also +@:TeXbook}{\sl The \TeX book@> +@.AmSTeX@> +@.plain@> +be provided for commonly used formats. + +@<Glob...@>= +static int @!ready_already; /*a sacrifice of purity for economy*/ + +@ Now this is really it: \TeX\ starts and ends here. + +The initial test involving |ready_already| should be deleted if the +\PASCAL\ runtime system is smart enough to detect such a ``mistake.'' +@^system dependencies@> + +@p +int main(int argc, char *argv[]) {@! /*|start_here|*/ + main_init(argc, argv); /* \TeX\ Live */ +history=fatal_error_stop; /*in case we quit during initialization*/ +t_open_out; /*open the terminal for output*/ +if (ready_already==314159) goto start_of_TEX; +@<Check the ``constant'' values...@>@; +if (bad > 0) + {@+wterm_ln("Ouch---my internal constants have been clobbered!" + "---case %d", bad); +@.Ouch...clobbered@> + exit(0); + } +get_strings_started(); +initialize(); /*set global variables to their starting values*/ +#ifdef @!INIT +if (iniversion) /* \TeX\ Live */ +{ init_prim(); /*call |primitive| for each primitive*/ + init_str_ptr=str_ptr;init_pool_ptr=pool_ptr;fix_date_and_time(); } +#endif +ready_already=314159; +start_of_TEX: @<Initialize the output routines@>; +@<Get the first line of input and prepare to start@>; +history=spotless; /*ready to go!*/ +main_control(); /*come to life*/ +final_cleanup(); /*prepare for death*/ +close_files_and_terminate(); +ready_already=0; +return 0; } + +@ Here we do whatever is needed to complete \TeX's job gracefully on the +local operating system. The code here might come into play after a fatal +error; it must therefore consist entirely of ``safe'' operations that +cannot produce error messages. For example, it would be a mistake to call +|str_room| or |make_string| at this time, because a call on |overflow| +might lead to an infinite loop. +@^system dependencies@> +(Actually there's one way to get error messages, via |prepare_mag|; +but that can't cause infinite recursion.) +@^recursion@> + +If |final_cleanup| is bypassed, this program doesn't bother to close +the input files that may still be open. + +@<Last-minute...@>= +static void close_files_and_terminate(void) +{@+int k; /*all-purpose index*/ +@<Finish the extensions@>;new_line_char=-1; +#ifdef @!STAT +if (tracing_stats > 0) @<Output statistics about this job@>;@; +#endif +wake_up_terminal;@<Finish the \.{DVI} file@>; +if (log_opened) + {@+wlog_cr;a_close(&log_file);selector=selector-2; + if (selector==term_only) + {@+print_nl("Transcript written on "); +@.Transcript written...@> + slow_print(log_name);print_char('.');print_nl(""); + } + } +} + +@ The present section goes directly to the log file instead of using +|print| commands, because there's no need for these strings to take +up |str_pool| memory when a non-{\bf stat} version of \TeX\ is being used. + +@<Output statistics...@>= +if (log_opened) + {@+wlog_ln(" "); + wlog_ln("Here is how much of TeX's memory you used:"); +@.Here is how much...@> + wlog(" %d string", str_ptr-init_str_ptr); + if (str_ptr!=init_str_ptr+1) wlog( "s" ); + wlog_ln( " out of %d", max_strings-init_str_ptr);@/ + wlog_ln( " %d string characters out of %d", pool_ptr-init_pool_ptr, + pool_size-init_pool_ptr);@/ + wlog_ln(" %d words of memory out of %d", lo_mem_max-mem_min+mem_end-hi_mem_min+2,@| + mem_end+1-mem_min);@/ + wlog_ln(" %d multiletter control sequences out of %d", cs_count, hash_size);@/ + wlog(" %d words of font info for %d font", fmem_ptr, font_ptr-font_base); + if (font_ptr!=font_base+1) wlog("s"); + wlog_ln( ", out of %d for %d", font_mem_size, font_max-font_base);@/ + wlog(" %d hyphenation exception", hyph_count); + if (hyph_count!=1) wlog("s"); + wlog_ln( " out of %d", hyph_size);@/ + wlog_ln(" %di,%dn,%dp,%db,%ds stack positions out of %di,%dn,%dp,%db,%ds", + max_in_stack, max_nest_stack,@| + max_param_stack,@| + max_buf_stack+1,@| + max_save_stack+6,@| + stack_size, + nest_size, + param_size, + buf_size, + save_size ); + } + +@ We get to the |final_cleanup| routine when \.{\\end} or \.{\\dump} has +been scanned and |its_all_over|\kern-2pt. + +@<Last-minute...@>= +static void final_cleanup(void) +{@+ +int c; /*0 for \.{\\end}, 1 for \.{\\dump}*/ +c=cur_chr;if (c!=1) new_line_char=-1; +if (job_name==0) open_log_file(); +while (input_ptr > 0) + if (state==token_list) end_token_list();@+else end_file_reading(); +while (open_parens > 0) + {@+print(" )");decr(open_parens); + } +if (cur_level > level_one) + {@+print_nl("(");print_esc("end occurred "); + print("inside a group at level "); +@:end\_}{\.{(\\end occurred...)}@> + print_int(cur_level-level_one);print_char(')'); + if (eTeX_ex) show_save_groups(); + } +while (cond_ptr!=null) + {@+print_nl("(");print_esc("end occurred "); + print("when ");print_cmd_chr(if_test, cur_if); + if (if_line!=0) + {@+print(" on line ");print_int(if_line); + } + print(" was incomplete)"); + if_line=if_line_field(cond_ptr); + cur_if=subtype(cond_ptr);temp_ptr=cond_ptr; + cond_ptr=link(cond_ptr);free_node(temp_ptr, if_node_size); + } +if (history!=spotless) + if (((history==warning_issued)||(interaction < error_stop_mode))) + if (selector==term_and_log) + {@+selector=term_only; + print_nl("(see the transcript file for additional information)"); +@.see the transcript file...@> + selector=term_and_log; + } +if (c==1) + { +#ifdef @!INIT +for (c=top_mark_code; c<=split_bot_mark_code; c++) + if (cur_mark[c]!=null) delete_token_ref(cur_mark[c]); + if (sa_mark!=null) + if (do_marks(destroy_marks, 0, sa_mark)) sa_mark=null; + for (c=last_box_code; c<=vsplit_code; c++) flush_node_list(disc_ptr[c]); + if (last_glue!=max_halfword) delete_glue_ref(last_glue); + store_fmt_file();return; +#endif + print_nl("(\\dump is performed only by INITEX)");return; +@:dump\_}{\.{\\dump...only by INITEX}@> + } +} + +@ @<Last-minute...@>= +#ifdef @!INIT +static void init_prim(void) /*initialize all the primitives*/ +{@+no_new_control_sequence=false; +first=0; +@<Put each...@>; +no_new_control_sequence=true; +} +#endif + +@ When we begin the following code, \TeX's tables may still contain garbage; +the strings might not even be present. Thus we must proceed cautiously to get +bootstrapped in. + +But when we finish this part of the program, \TeX\ is ready to call on the +|main_control| routine to do its work. + +@<Get the first line...@>= +{@+@<Initialize the input routines@>; +@<Enable \eTeX\ and furthermore Prote, if requested@>@;@/ +if ((format_ident==0)||(buffer[loc]=='&')) + {@+if (format_ident!=0) initialize(); /*erase preloaded format*/ + if (!open_fmt_file()) exit(0); + if (!load_fmt_file()) + {@+w_close(&fmt_file);exit(0); + } + w_close(&fmt_file); + while ((loc < limit)&&(buffer[loc]==' ')) incr(loc); + } +if (eTeX_ex) wterm_ln("entering extended mode"); +if (Prote_ex) {@+Prote_initialize(); + } +if (end_line_char_inactive) decr(limit); +else buffer[limit]=end_line_char; +fix_date_and_time();@/ +@<Initialize the print |selector|...@>; +if ((loc < limit)&&(cat_code(buffer[loc])!=escape)) start_input(); + /*\.{\\input} assumed*/ +} + +@* Debugging. +Once \TeX\ is working, you should be able to diagnose most errors with +the \.{\\show} commands and other diagnostic features. But for the initial +stages of debugging, and for the revelation of really deep mysteries, you +can compile \TeX\ with a few more aids, including the \PASCAL\ runtime +checks and its debugger. An additional routine called |debug_help| +will also come into play when you type `\.D' after an error message; +|debug_help| also occurs just before a fatal error causes \TeX\ to succumb. +@^debugging@> +@^system dependencies@> + +The interface to |debug_help| is primitive, but it is good enough when used +with a \PASCAL\ debugger that allows you to set breakpoints and to read +variables and change their values. After getting the prompt `\.{debug \#}', you +type either a negative number (this exits |debug_help|), or zero (this +goes to a location where you can set a breakpoint, thereby entering into +dialog with the \PASCAL\ debugger), or a positive number |m| followed by +an argument |n|. The meaning of |m| and |n| will be clear from the +program below. (If |m==13|, there is an additional argument, |l|.) +@.debug \#@> + +@<Last-minute...@>= +#ifdef @!DEBUG +static void debug_help(void) /*routine to display various things*/ +{@+ +int k, @!l, @!m, @!n; +clear_terminal; + loop{@+wake_up_terminal; + print_nl("debug # (-1 to exit):");update_terminal; +@.debug \#@> + if (fscanf(term_in.f," %d",&m)<1 || + m < 0) return; + else if (m==0) + {@+goto breakpoint;@/ /*go to every declared label at least once*/ + breakpoint: m=0; /*'BREAKPOINT'*/@/ + } + else{@+fscanf(term_in.f," %d",&n); + switch (m) { + @t\4@>@<Numbered cases for |debug_help|@>@; + default:print("?"); + } + } + } +} +#endif + +@ @<Numbered cases...@>= +case 1: print_word(mem[n]);@+break; /*display |mem[n]| in all forms*/ +case 2: print_int(info(n));@+break; +case 3: print_int(link(n));@+break; +case 4: print_word(eqtb[n]);@+break; +case 5: print_word(font_info[n]);@+break; +case 6: print_word(save_stack[n]);@+break; +case 7: show_box(n);@+break; + /*show a box, abbreviated by |show_box_depth| and |show_box_breadth|*/ +case 8: {@+breadth_max=10000;depth_threshold=pool_size-pool_ptr-10; + show_node_list(n); /*show a box in its entirety*/ + } @+break; +case 9: show_token_list(n, null, 1000);@+break; +case 10: slow_print(n);@+break; +case 11: check_mem(n > 0);@+break; /*check wellformedness; print new busy locations if |n > 0|*/ +case 12: search_mem(n);@+break; /*look for pointers to |n|*/ +case 13: {@+fscanf(term_in.f," %d",&l);print_cmd_chr(n, l); + } @+break; +case 14: for (k=0; k<=n; k++) printn(buffer[k]);@+break; +case 15: {@+font_in_short_display=null_font;short_display(n); + } @+break; +case 16: panicking=!panicking;@+break; + +@* Extensions. +The program above includes a bunch of ``hooks'' that allow further +capabilities to be added without upsetting \TeX's basic structure. +Most of these hooks are concerned with ``whatsit'' nodes, which are +intended to be used for special purposes; whenever a new extension to +\TeX\ involves a new kind of whatsit node, a corresponding change needs +to be made to the routines below that deal with such nodes, +but it will usually be unnecessary to make many changes to the +other parts of this program. + +In order to demonstrate how extensions can be made, we shall treat +`\.{\\write}', `\.{\\openout}', `\.{\\closeout}', `\.{\\immediate}', +`\.{\\special}', and `\.{\\setlanguage}' as if they were extensions. +These commands are actually primitives of \TeX, and they should +appear in all implementations of the system; but let's try to imagine +that they aren't. Then the program below illustrates how a person +could add them. + +Sometimes, of course, an extension will require changes to \TeX\ itself; +no system of hooks could be complete enough for all conceivable extensions. +The features associated with `\.{\\write}' are almost all confined to the +following paragraphs, but there are small parts of the |print_ln| and +|print_char| procedures that were introduced specifically to \.{\\write} +characters. Furthermore one of the token lists recognized by the scanner +is a |write_text|; and there are a few other miscellaneous places where we +have already provided for some aspect of \.{\\write}. The goal of a \TeX\ +extender should be to minimize alterations to the standard parts of the +program, and to avoid them completely if possible. He or she should also +be quite sure that there's no easy way to accomplish the desired goals +with the standard features that \TeX\ already has. ``Think thrice before +extending,'' because that may save a lot of work, and it will also keep +incompatible extensions of \TeX\ from proliferating. +@^system dependencies@> +@^extensions to \TeX@> + +@ First let's consider the format of whatsit nodes that are used to represent +the data associated with \.{\\write} and its relatives. Recall that a whatsit +has |type==whatsit_node|, and the |subtype| is supposed to distinguish +different kinds of whatsits. Each node occupies two or more words; the +exact number is immaterial, as long as it is readily determined from the +|subtype| or other data. + +We shall introduce five |subtype| values here, corresponding to the +control sequences \.{\\openout}, \.{\\write}, \.{\\closeout}, \.{\\special}, and +\.{\\setlanguage}. The second word of I/O whatsits has a |write_stream| field +that identifies the write-stream number (0 to 15, or 16 for out-of-range and +positive, or 17 for out-of-range and negative). +In the case of \.{\\write} and \.{\\special}, there is also a field that +points to the reference count of a token list that should be sent. In the +case of \.{\\openout}, we need three words and three auxiliary subfields +to hold the string numbers for name, area, and extension. + +@d write_node_size 2 /*number of words in a write/whatsit node*/ +@d open_node_size 3 /*number of words in an open/whatsit node*/ +@d open_node 0 /*|subtype| in whatsits that represent files to \.{\\openout}*/ +@d write_node 1 /*|subtype| in whatsits that represent things to \.{\\write}*/ +@d close_node 2 /*|subtype| in whatsits that represent streams to \.{\\closeout}*/ +@d special_node 3 /*|subtype| in whatsits that represent \.{\\special} things*/ +@d language_node 4 /*|subtype| in whatsits that change the current language*/ +@d what_lang(A) link(A+1) /*language number, in the range |0 dotdot 255|*/ +@d what_lhm(A) type(A+1) /*minimum left fragment, in the range |1 dotdot 63|*/ +@d what_rhm(A) subtype(A+1) /*minimum right fragment, in the range |1 dotdot 63|*/ +@d write_tokens(A) link(A+1) /*reference count of token list to write*/ +@d write_stream(A) info(A+1) /*stream number (0 to 17)*/ +@d open_name(A) link(A+1) /*string number of file name to open*/ +@d open_area(A) info(A+2) /*string number of file area for |open_name|*/ +@d open_ext(A) link(A+2) /*string number of file extension for |open_name|*/ + +@ The sixteen possible \.{\\write} streams are represented by the |write_file| +array. The |j|th file is open if and only if |write_open[j]==true|. The last +two streams are special; |write_open[16]| represents a stream number +greater than 15, while |write_open[17]| represents a negative stream number, +and both of these variables are always |false|. + +@<Glob...@>= +static alpha_file @!write_file[16]; +static bool @!write_open[18]; + +@ @<Set init...@>= +for (k=0; k<=17; k++) write_open[k]=false; + +@ Extensions might introduce new command codes; but it's best to use +|extension| with a modifier, whenever possible, so that |main_control| +stays the same. + +@d immediate_code 4 /*command modifier for \.{\\immediate}*/ +@d latex_first_extension_code 5 +@d latespecial_node (latex_first_extension_code+0) /*|subtype| in whatsits that represent \.{\\special} things expanded during output*/ +@d set_language_code (latex_first_extension_code+1) /*command modifier for \.{\\setlanguage}*/ +@d TeX_last_extension_cmd_mod set_language_code + +@<Put each...@>= +primitive("openout", extension, open_node);@/ +@!@:open\_out\_}{\.{\\openout} primitive@> +primitive("write", extension, write_node);write_loc=cur_val;@/ +@!@:write\_}{\.{\\write} primitive@> +primitive("closeout", extension, close_node);@/ +@!@:close\_out\_}{\.{\\closeout} primitive@> +primitive("special", extension, special_node);@/ +@!@:special\_}{\.{\\special} primitive@> +primitive("immediate", extension, immediate_code);@/ +@!@:immediate\_}{\.{\\immediate} primitive@> +primitive("setlanguage", extension, set_language_code);@/ +@!@:set\_language\_}{\.{\\setlanguage} primitive@> + +@ The variable |write_loc| just introduced is used to provide an +appropriate error message in case of ``runaway'' write texts. + +@<Glob...@>= +static pointer @!write_loc; /*|eqtb| address of \.{\\write}*/ + +@ @<Cases of |print_cmd_chr|...@>= +case extension: switch (chr_code) { + case open_node: print_esc("openout");@+break; + case write_node: print_esc("write");@+break; + case close_node: print_esc("closeout");@+break; + case special_node: print_esc("special");@+break; + case immediate_code: print_esc("immediate");@+break; + case set_language_code: print_esc("setlanguage");@+break; + @/@<Cases of |extension| for |print_cmd_chr|@>@/ + default:print("[unknown extension!]"); + } @+break; + + + +@ When an |extension| command occurs in |main_control|, in any mode, +the |do_extension| routine is called. + +@<Cases of |main_control| that are for extensions...@>= +any_mode(extension): do_extension(); + +@ @<Declare act...@>= +@t\4@>@<Declare procedures needed in |do_extension|@>@; +static void do_extension(void) +{@+int @!k; /*all-purpose integer*/ +pointer @!p; /*all-purpose pointer*/ +switch (cur_chr) { +case open_node: @<Implement \.{\\openout}@>@;@+break; +case write_node: @<Implement \.{\\write}@>@;@+break; +case close_node: @<Implement \.{\\closeout}@>@;@+break; +case special_node: @<Implement \.{\\special}@>@;@+break; +case immediate_code: @<Implement \.{\\immediate}@>@;@+break; +case set_language_code: @<Implement \.{\\setlanguage}@>@;@+break; +@/@<Cases for |do_extension|@>@/ +default:confusion("ext1"); +@:this can't happen ext1}{\quad ext1@> +} +} + +@ Here is a subroutine that creates a whatsit node having a given |subtype| +and a given number of words. It initializes only the first word of the whatsit, +and appends it to the current list. + +@<Declare procedures needed in |do_extension|@>= +static void new_whatsit(small_number @!s, small_number @!w) +{@+pointer p; /*the new node*/ +p=get_node(w);type(p)=whatsit_node;subtype(p)=s; +link(tail)=p;tail=p; +} + +@ The next subroutine uses |cur_chr| to decide what sort of whatsit is +involved, and also inserts a |write_stream| number. + +@<Declare procedures needed in |do_ext...@>= +static void new_write_whatsit(small_number @!w) +{@+new_whatsit(cur_chr, w); +if (w!=write_node_size) scan_four_bit_int(); +else{@+scan_int(); + if (cur_val < 0) cur_val=17; + else if (cur_val > 15) cur_val=16; + } +write_stream(tail)=cur_val; +} + +@ @<Implement \.{\\openout}@>= +{@+new_write_whatsit(open_node_size); +scan_optional_equals();scan_file_name();@/ +open_name(tail)=cur_name;open_area(tail)=cur_area;open_ext(tail)=cur_ext; +} + +@ When `\.{\\write 12\{...\}}' appears, we scan the token list `\.{\{...\}}' +without expanding its macros; the macros will be expanded later when this +token list is rescanned. + +@<Implement \.{\\write}@>= +{@+k=cur_cs;new_write_whatsit(write_node_size);@/ +cur_cs=k;p=scan_toks(false, false);write_tokens(tail)=def_ref; +} + +@ @<Implement \.{\\closeout}@>= +{@+new_write_whatsit(write_node_size);write_tokens(tail)=null; +} + +@ When `\.{\\special\{...\}}' appears, we expand the macros in the token +list as in \.{\\xdef} and \.{\\mark}. When marked with \.{shipout}, we keep +tokens unexpanded for now. + +@<Implement \.{\\special}@>= +{@+if (scan_keyword("shipout")) +{@+new_whatsit(latespecial_node, write_node_size);write_stream(tail)=null; +p=scan_toks(false, false);write_tokens(tail)=def_ref; +} else +{@+new_whatsit(special_node, write_node_size);write_stream(tail)=null; +p=scan_toks(false, true);write_tokens(tail)=def_ref; +} } + +@ Each new type of node that appears in our data structure must be capable +of being displayed, copied, destroyed, and so on. The routines that we +need for write-oriented whatsits are somewhat like those for mark nodes; +other extensions might, of course, involve more subtlety here. + +@<Basic printing...@>= +static void print_write_whatsit(char *@!s, pointer @!p) +{@+print_esc(s); +if (write_stream(p) < 16) print_int(write_stream(p)); +else if (write_stream(p)==16) print_char('*'); +@.*\relax@> +else print_char('-'); +} + +@ @<Display the whatsit...@>= +switch (subtype(p)) { +case open_node: {@+print_write_whatsit("openout", p); + print_char('=');print_file_name(open_name(p), open_area(p), open_ext(p)); + } @+break; +case write_node: {@+print_write_whatsit("write", p); + print_mark(write_tokens(p)); + } @+break; +case close_node: print_write_whatsit("closeout", p);@+break; +case latespecial_node: {@+print_esc("special");print(" shipout"); + print_mark(write_tokens(p)); + } @+break; +case special_node: {@+print_esc("special"); + print_mark(write_tokens(p)); + } @+break; +case language_node: {@+print_esc("setlanguage"); + print_int(what_lang(p));print(" (hyphenmin "); + print_int(what_lhm(p));print_char(','); + print_int(what_rhm(p));print_char(')'); + } @+break; +@/@<Cases for displaying the |whatsit| node@>@/ +default:print("whatsit?"); +} + +@ @<Make a partial copy of the whatsit...@>= +switch (subtype(p)) { +case open_node: {@+r=get_node(open_node_size);words=open_node_size; + } @+break; +case write_node: case special_node: case latespecial_node: {@+r=get_node(write_node_size); + add_token_ref(write_tokens(p));words=write_node_size; + } @+break; +case close_node: case language_node: {@+r=get_node(small_node_size); + words=small_node_size; + } @+break; +@/@<Cases for making a partial copy of the whatsit node@>@/ +default:confusion("ext2"); +@:this can't happen ext2}{\quad ext2@> +} + +@ @<Wipe out the whatsit...@>= +{@+switch (subtype(p)) { +case open_node: free_node(p, open_node_size);@+break; +case write_node: case special_node: case latespecial_node: {@+delete_token_ref(write_tokens(p)); + free_node(p, write_node_size);goto done; + } +case close_node: case language_node: free_node(p, small_node_size);@+break; +@/@<Cases for wiping out the whatsit node@>@/ +default:confusion("ext3"); +@:this can't happen ext3}{\quad ext3@> +} @/ +goto done; +} + +@ @<Incorporate a whatsit node into a vbox@>=do_nothing + +@ @<Incorporate a whatsit node into an hbox@>=do_nothing + +@ @<Let |d| be the width of the whatsit |p|@>=d=0 + +@ @d adv_past(A) @+if (subtype(A)==language_node) + {@+cur_lang=what_lang(A);l_hyf=what_lhm(A);r_hyf=what_rhm(A); + set_hyph_index; + } + +@<Advance \(p)past a whatsit node in the \(l)|line_break| loop@>=@+ +adv_past(cur_p) + +@ @<Advance \(p)past a whatsit node in the \(p)pre-hyphenation loop@>=@+ +adv_past(s) + +@ @<Prepare to move whatsit |p| to the current page, then |goto contribute|@>= +goto contribute + +@ @<Process whatsit |p| in |vert_break| loop, |goto not_found|@>= +goto not_found + +@ @<Output the whatsit node |p| in a vlist@>= +out_what(p) + +@ @<Output the whatsit node |p| in an hlist@>= +out_what(p) + +@ After all this preliminary shuffling, we come finally to the routines +that actually send out the requested data. Let's do \.{\\special} first +(it's easier). + +@<Declare procedures needed in |hlist_out|, |vlist_out|@>= +static void special_out(pointer @!p) +{@+pointer @!q, @!r; /*temporary variables for list manipulation*/ +int @!old_mode; /*saved |mode|*/ + +if (subtype(p)==latespecial_node) + {@+@<Expand macros in the token list and make |link(def_ref)| point to the +result@>; + write_tokens(p)=def_ref; + } +} + +@ To write a token list, we must run it through \TeX's scanner, expanding +macros and \.{\\the} and \.{\\number}, etc. This might cause runaways, +if a delimited macro parameter isn't matched, and runaways would be +extremely confusing since we are calling on \TeX's scanner in the middle +of a \.{\\shipout} command. Therefore we will put a dummy control sequence as +a ``stopper,'' right after the token list. This control sequence is +artificially defined to be \.{\\outer}. +@:end\_write\_}{\.{\\endwrite}@> + +@<Initialize table...@>= +text(end_write)=s_no("endwrite");eq_level(end_write)=level_one; +eq_type(end_write)=outer_call;equiv(end_write)=null; + +@ @<Declare procedures needed in |hlist_out|, |vlist_out|@>= +static void write_out(pointer @!p) +{@+int old_setting; /*holds print |selector|*/ +int @!old_mode; /*saved |mode|*/ +small_number @!j; /*write stream number*/ +pointer @!q, @!r; /*temporary variables for list manipulation*/ +@<Expand macros in the token list and make |link(def_ref)| point to the result@>; +old_setting=selector;j=write_stream(p); +if (write_open[j]) selector=j; +else{@+ /*write to the terminal if file isn't open*/ + if ((j==17)&&(selector==term_and_log)) selector=log_only; + print_nl(""); + } +token_show(def_ref);print_ln(); +flush_list(def_ref);selector=old_setting; +} + +@ The final line of this routine is slightly subtle; at least, the author +didn't think about it until getting burnt! There is a used-up token list +@^Knuth, Donald Ervin@> +on the stack, namely the one that contained |end_write_token|. (We +insert this artificial `\.{\\endwrite}' to prevent runaways, as explained +above.) If it were not removed, and if there were numerous writes on a +single page, the stack would overflow. + +@d end_write_token cs_token_flag+end_write + +@<Expand macros in the token list and...@>= +q=get_avail();info(q)=right_brace_token+'}';@/ +fl_mem[q]=FILE_LINE(system_file,system_insert); +r=get_avail();link(q)=r;info(r)=end_write_token;ins_list(q);@/ +fl_mem[r]=FILE_LINE(system_file,system_insert); +begin_token_list(write_tokens(p), write_text);@/ +q=get_avail();info(q)=left_brace_token+'{';ins_list(q); +fl_mem[q]=FILE_LINE(system_file,system_insert); + + /*now we're ready to scan + `\.\{$\langle\,$token list$\,\rangle$\.{\} \\endwrite}'*/ +old_mode=mode;mode=0; + /*disable \.{\\prevdepth}, \.{\\spacefactor}, \.{\\lastskip}, \.{\\prevgraf}*/ +cur_cs=write_loc;q=scan_toks(false, true); /*expand macros, etc.*/ +get_token();@+if (cur_tok!=end_write_token) + @<Recover from an unbalanced write command@>; +mode=old_mode; +end_token_list() /*conserve stack space*/ + +@ @<Recover from an unbalanced write command@>= +{@+print_err("Unbalanced write command"); +@.Unbalanced write...@> +help2("On this page there's a \\write with fewer real {'s than }'s.",@/ +"I can't handle that very well; good luck.");error(); +@/do@+{get_token(); +}@+ while (!(cur_tok==end_write_token)); +} + +@ The |out_what| procedure takes care of outputting whatsit nodes for +|vlist_out| and |hlist_out|\kern-.3pt. + +@<Declare procedures needed in |hlist_out|, |vlist_out|@>= +@t\4@>@<Declare procedures needed in |out_what|@>@; +static void out_what(pointer @!p) +{@+small_number j; /*write stream number*/ +switch (subtype(p)) { +case open_node: case write_node: case close_node: @<Do some work that has +been queued up for \.{\\write}@>@;@+break; +case special_node: special_out(p);@+break; +case language_node: do_nothing;@+break; +@/@<Cases for |out_what|@>@/ +default:confusion("ext4"); +@:this can't happen ext4}{\quad ext4@> +} +} + +@ We don't implement \.{\\write} inside of leaders. (The reason is that +the number of times a leader box appears might be different in different +implementations, due to machine-dependent rounding in the glue calculations.) +@^leaders@> + +@<Do some work that has been queued up...@>= +if (!doing_leaders) + {@+j=write_stream(p); + if (subtype(p)==write_node) write_out(p); + else{@+if (write_open[j]) a_close(&write_file[j]); + if (subtype(p)==close_node) write_open[j]=false; + else if (j < 16) + {@+cur_name=open_name(p);cur_area=open_area(p); + cur_ext=open_ext(p); + pack_cur_name(".tex"); + while (!a_open_out(&write_file[j])) + prompt_file_name("output file name",".tex"); + write_open[j]=true; + } + } + } + +@ The presence of `\.{\\immediate}' causes the |do_extension| procedure +to descend to one level of recursion. Nothing happens unless \.{\\immediate} +is followed by `\.{\\openout}', `\.{\\write}', or `\.{\\closeout}'. +@^recursion@> + +@<Implement \.{\\immediate}@>= +{@+get_x_token(); +if ((cur_cmd==extension)&&(cur_chr <= close_node)) + {@+p=tail;do_extension(); /*append a whatsit node*/ + out_what(tail); /*do the action immediately*/ + flush_node_list(tail);tail=p;link(p)=null; + } +else back_input(); +} + +@ The \.{\\language} extension is somewhat different. +We need a subroutine that comes into play when a character of +a non-|clang| language is being appended to the current paragraph. + +@<Declare action...@>= +static void fix_language(void) +{@+ASCII_code @!l; /*the new current language*/ +if (language <= 0) l=0; +else if (language > 255) l=0; +else l=language; +if (l!=clang) + {@+new_whatsit(language_node, small_node_size); + what_lang(tail)=l;clang=l;@/ + what_lhm(tail)=norm_min(left_hyphen_min); + what_rhm(tail)=norm_min(right_hyphen_min); + } +} + +@ @<Implement \.{\\setlanguage}@>= +if (abs(mode)!=hmode) report_illegal_case(); +else{@+new_whatsit(language_node, small_node_size); + scan_int(); + if (cur_val <= 0) clang=0; + else if (cur_val > 255) clang=0; + else clang=cur_val; + what_lang(tail)=clang; + what_lhm(tail)=norm_min(left_hyphen_min); + what_rhm(tail)=norm_min(right_hyphen_min); + } + +@ @<Finish the extensions@>= +for (k=0; k<=15; k++) if (write_open[k]) a_close(&write_file[k]); + +@* The extended features of \eTeX. +The program has three modes of operation: (1)~In \TeX\ compatibility mode +it fully deserves the name \TeX\ and there are neither extended features +nor additional primitive commands. There are, however, a few +modifications that would be legitimate in any implementation of \TeX\ +such as, e.g., preventing inadequate results of the glue to \.{DVI} +unit conversion during |ship_out|. (2)~In extended mode there are +additional primitive commands and the extended features of \eTeX\ are +available. (3)~In \Prote\ mode there are supplementary primitive +commands that will be discussed in the section below. + +The distinction between these three modes of operation initially takes +place when a `virgin' \.{eINITEX} starts without reading a format file. +Later on the values of all \eTeX\ state variables are inherited when +\.{eVIRTEX} (or \.{eINITEX}) reads a format file. + +The code below is designed to work for cases where `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$' +is a run-time switch. + +@<Enable \eTeX\ and furthermore Prote, if requested@>= +#ifdef @!INIT +if (iniversion && (buffer[loc]=='*'||etexp)) /* \TeX\ Live */ + {@+no_new_control_sequence=false; + @<Generate all \eTeX\ primitives@>@; + if (buffer[loc]=='*') incr(loc); /* \TeX\ Live */ + eTeX_mode=1; /*enter extended mode*/ + @<Initialize variables for \eTeX\ extended mode@>@; + if (buffer[loc]=='*'||ltxp) {@+ + @<Check \Prote\ ``constant'' values for consistency@>@; + @<Generate all \Prote\ primitives@>@; + if (buffer[loc]=='*')incr(loc); + Prote_mode=1; /*enter \Prote\ mode*/ + } + } +#endif +@;@/ +if (!no_new_control_sequence) /*just entered extended mode ?*/ + no_new_control_sequence=true;@+else + +@ The \eTeX\ features available in extended mode are grouped into two +categories: (1)~Some of them are permanently enabled and have no +semantic effect as long as none of the additional primitives are +executed. (2)~The remaining \eTeX\ features are optional and can be +individually enabled and disabled. For each optional feature there is +an \eTeX\ state variable named \.{\\...state}; the feature is enabled, +resp.\ disabled by assigning a positive, resp.\ non-positive value to +that integer. + +@d eTeX_state_base (int_base+eTeX_state_code) +@d eTeX_state(A) eqtb[eTeX_state_base+A].i /*an \eTeX\ state variable*/ +@# +@d eTeX_version_code eTeX_int /*code for \.{\\eTeXversion}*/ + +@<Generate all \eTeX...@>= +primitive("lastnodetype", last_item, last_node_type_code); +@!@:last\_node\_type\_}{\.{\\lastnodetype} primitive@> +primitive("eTeXversion", last_item, eTeX_version_code); +@!@:eTeX\_version\_}{\.{\\eTeXversion} primitive@> +primitive("eTeXrevision", convert, eTeX_revision_code);@/ +@!@:eTeX\_revision\_}{\.{\\eTeXrevision} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case last_node_type_code: print_esc("lastnodetype");@+break; +case eTeX_version_code: print_esc("eTeXversion");@+break; + +@ @<Cases for fetching an integer value@>= +case eTeX_version_code: cur_val=eTeX_version;@+break; + +@ @d eTeX_ex (eTeX_mode==1) /*is this extended mode?*/ + +@<Glob...@>= +static int @!eTeX_mode; /*identifies compatibility and extended mode*/ + +@ @<Initialize table entries...@>= +eTeX_mode=0; /*initially we are in compatibility mode*/ +@<Initialize variables for \eTeX\ compatibility mode@>@; + +@ @<Dump the \eTeX\ state@>= +dump_int(eTeX_mode); +for (j=0; j<=eTeX_states-1; j++) eTeX_state(j)=0; /*disable all enhancements*/ + +@ @<Undump the \eTeX\ state@>= +undump(0, 1, eTeX_mode); +if (eTeX_ex) + {@+@<Initialize variables for \eTeX\ extended mode@>; + } +else{@+@<Initialize variables for \eTeX\ compatibility mode@>; + } + +@ The |eTeX_enabled| function simply returns its first argument as +result. This argument is |true| if an optional \eTeX\ feature is +currently enabled; otherwise, if the argument is |false|, the function +gives an error message. + +@<Declare \eTeX\ procedures for use...@>= +static bool eTeX_enabled(bool @!b, quarterword @!j, halfword @!k) +{@+if (!b) + {@+print_err("Improper ");print_cmd_chr(j, k); + help1("Sorry, this optional e-TeX feature has been disabled.");error(); + } +return b; +} + +@ First we implement the additional \eTeX\ parameters in the table of +equivalents. + +@<Generate all \eTeX...@>= +primitive("everyeof", assign_toks, every_eof_loc); +@!@:every\_eof\_}{\.{\\everyeof} primitive@> +primitive("tracingassigns", assign_int, int_base+tracing_assigns_code);@/ +@!@:tracing\_assigns\_}{\.{\\tracingassigns} primitive@> +primitive("tracinggroups", assign_int, int_base+tracing_groups_code);@/ +@!@:tracing\_groups\_}{\.{\\tracinggroups} primitive@> +primitive("tracingifs", assign_int, int_base+tracing_ifs_code);@/ +@!@:tracing\_ifs\_}{\.{\\tracingifs} primitive@> +primitive("tracingscantokens", assign_int, int_base+tracing_scan_tokens_code);@/ +@!@:tracing\_scan\_tokens\_}{\.{\\tracingscantokens} primitive@> +primitive("tracingnesting", assign_int, int_base+tracing_nesting_code);@/ +@!@:tracing\_nesting\_}{\.{\\tracingnesting} primitive@> +primitive("savingvdiscards", assign_int, int_base+saving_vdiscards_code);@/ +@!@:saving\_vdiscards\_}{\.{\\savingvdiscards} primitive@> +primitive("savinghyphcodes", assign_int, int_base+saving_hyph_codes_code);@/ +@!@:saving\_hyph\_codes\_}{\.{\\savinghyphcodes} primitive@> + +@ @d every_eof equiv(every_eof_loc) + +@<Cases of |assign_toks| for |print_cmd_chr|@>= +case every_eof_loc: print_esc("everyeof");@+break; + +@ @<Cases for |print_param|@>= +case tracing_assigns_code: print_esc("tracingassigns");@+break; +case tracing_groups_code: print_esc("tracinggroups");@+break; +case tracing_ifs_code: print_esc("tracingifs");@+break; +case tracing_scan_tokens_code: print_esc("tracingscantokens");@+break; +case tracing_nesting_code: print_esc("tracingnesting");@+break; +case saving_vdiscards_code: print_esc("savingvdiscards");@+break; +case saving_hyph_codes_code: print_esc("savinghyphcodes");@+break; + +@ In order to handle \.{\\everyeof} we need an array |eof_seen| of +boolean variables. + +@<Glob...@>= +static bool @!eof_seen0[max_in_open], + *const @!eof_seen = @!eof_seen0-1; /*has eof been seen?*/ + +@ The |print_group| procedure prints the current level of grouping and +the name corresponding to |cur_group|. + +@<Declare \eTeX\ procedures for tr...@>= +static void print_group(bool @!e) +{@+ +switch (cur_group) { + case bottom_level: {@+print("bottom level");return; + } + case simple_group: case semi_simple_group: + {@+if (cur_group==semi_simple_group) print("semi "); + print("simple"); + } @+break; + case hbox_group: case adjusted_hbox_group: + {@+if (cur_group==adjusted_hbox_group) print("adjusted "); + print("hbox"); + } @+break; + case vbox_group: print("vbox");@+break; + case vtop_group: print("vtop");@+break; + case align_group: case no_align_group: + {@+if (cur_group==no_align_group) print("no "); + print("align"); + } @+break; + case output_group: print("output");@+break; + case disc_group: print("disc");@+break; + case insert_group: print("insert");@+break; + case vcenter_group: print("vcenter");@+break; + case math_group: case math_choice_group: + case math_shift_group: case math_left_group: + {@+print("math"); + if (cur_group==math_choice_group) print(" choice"); + else if (cur_group==math_shift_group) print(" shift"); + else if (cur_group==math_left_group) print(" left"); + } + } /*there are no other cases*/ +print(" group (level ");print_int(qo(cur_level));print_char(')'); +if (saved(-1)!=0) + {@+if (e) print(" entered at line ");else print(" at line "); + print_int(saved(-1)); + } +} + +@ The |group_trace| procedure is called when a new level of grouping +begins (|e==false|) or ends (|e==true|) with |saved(-1)| containing the +line number. + +@<Declare \eTeX\ procedures for tr...@>= +#ifdef @!STAT +static void group_trace(bool @!e) +{@+begin_diagnostic();print_char('{'); +if (e) print("leaving ");else print("entering "); +print_group(e);print_char('}');end_diagnostic(false); +} +#endif + +@ The \.{\\currentgrouplevel} and \.{\\currentgrouptype} commands return +the current level of grouping and the type of the current group +respectively. + +@d current_group_level_code (eTeX_int+1) /*code for \.{\\currentgrouplevel}*/ +@d current_group_type_code (eTeX_int+2) /*code for \.{\\currentgrouptype}*/ + +@<Generate all \eTeX...@>= +primitive("currentgrouplevel", last_item, current_group_level_code); +@!@:current\_group\_level\_}{\.{\\currentgrouplevel} primitive@> +primitive("currentgrouptype", last_item, current_group_type_code); +@!@:current\_group\_type\_}{\.{\\currentgrouptype} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case current_group_level_code: print_esc("currentgrouplevel");@+break; +case current_group_type_code: print_esc("currentgrouptype");@+break; + +@ @<Cases for fetching an integer value@>= +case current_group_level_code: cur_val=cur_level-level_one;@+break; +case current_group_type_code: cur_val=cur_group;@+break; + +@ The \.{\\currentiflevel}, \.{\\currentiftype}, and +\.{\\currentifbranch} commands return the current level of conditionals +and the type and branch of the current conditional. + +@d current_if_level_code (eTeX_int+3) /*code for \.{\\currentiflevel}*/ +@d current_if_type_code (eTeX_int+4) /*code for \.{\\currentiftype}*/ +@d current_if_branch_code (eTeX_int+5) /*code for \.{\\currentifbranch}*/ + +@<Generate all \eTeX...@>= +primitive("currentiflevel", last_item, current_if_level_code); +@!@:current\_if\_level\_}{\.{\\currentiflevel} primitive@> +primitive("currentiftype", last_item, current_if_type_code); +@!@:current\_if\_type\_}{\.{\\currentiftype} primitive@> +primitive("currentifbranch", last_item, current_if_branch_code); +@!@:current\_if\_branch\_}{\.{\\currentifbranch} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case current_if_level_code: print_esc("currentiflevel");@+break; +case current_if_type_code: print_esc("currentiftype");@+break; +case current_if_branch_code: print_esc("currentifbranch");@+break; + +@ @<Cases for fetching an integer value@>= +case current_if_level_code: {@+q=cond_ptr;cur_val=0; + while (q!=null) + {@+incr(cur_val);q=link(q); + } + } @+break; +case current_if_type_code: if (cond_ptr==null) cur_val=0; + else if (cur_if < unless_code) cur_val=cur_if+1; + else cur_val=-(cur_if-unless_code+1);@+break; +case current_if_branch_code: + if ((if_limit==or_code)||(if_limit==else_code)) cur_val=1; + else if (if_limit==fi_code) cur_val=-1; + else cur_val=0;@+break; + +@ The \.{\\fontcharwd}, \.{\\fontcharht}, \.{\\fontchardp}, and +\.{\\fontcharic} commands return information about a character in a +font. + +@d font_char_wd_code eTeX_dim /*code for \.{\\fontcharwd}*/ +@d font_char_ht_code (eTeX_dim+1) /*code for \.{\\fontcharht}*/ +@d font_char_dp_code (eTeX_dim+2) /*code for \.{\\fontchardp}*/ +@d font_char_ic_code (eTeX_dim+3) /*code for \.{\\fontcharic}*/ + +@<Generate all \eTeX...@>= +primitive("fontcharwd", last_item, font_char_wd_code); +@!@:font\_char\_wd\_}{\.{\\fontcharwd} primitive@> +primitive("fontcharht", last_item, font_char_ht_code); +@!@:font\_char\_ht\_}{\.{\\fontcharht} primitive@> +primitive("fontchardp", last_item, font_char_dp_code); +@!@:font\_char\_dp\_}{\.{\\fontchardp} primitive@> +primitive("fontcharic", last_item, font_char_ic_code); +@!@:font\_char\_ic\_}{\.{\\fontcharic} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case font_char_wd_code: print_esc("fontcharwd");@+break; +case font_char_ht_code: print_esc("fontcharht");@+break; +case font_char_dp_code: print_esc("fontchardp");@+break; +case font_char_ic_code: print_esc("fontcharic");@+break; + +@ @<Cases for fetching a dimension value@>= +case font_char_wd_code: +case font_char_ht_code: +case font_char_dp_code: +case font_char_ic_code: {@+scan_font_ident();q=cur_val;scan_char_num(); + if ((font_bc[q] <= cur_val)&&(font_ec[q] >= cur_val)) + {@+i=char_info(q, qi(cur_val)); + switch (m) { + case font_char_wd_code: cur_val=char_width(q, i);@+break; + case font_char_ht_code: cur_val=char_height(q, height_depth(i));@+break; + case font_char_dp_code: cur_val=char_depth(q, height_depth(i));@+break; + case font_char_ic_code: cur_val=char_italic(q, i); + } /*there are no other cases*/ + } + else cur_val=0; + } @+break; + +@ The \.{\\parshapedimen}, \.{\\parshapeindent}, and \.{\\parshapelength} +commands return the indent and length parameters of the current +\.{\\parshape} specification. + +@d par_shape_length_code (eTeX_dim+4) /*code for \.{\\parshapelength}*/ +@d par_shape_indent_code (eTeX_dim+5) /*code for \.{\\parshapeindent}*/ +@d par_shape_dimen_code (eTeX_dim+6) /*code for \.{\\parshapedimen}*/ + +@<Generate all \eTeX...@>= +primitive("parshapelength", last_item, par_shape_length_code); +@!@:par\_shape\_length\_}{\.{\\parshapelength} primitive@> +primitive("parshapeindent", last_item, par_shape_indent_code); +@!@:par\_shape\_indent\_}{\.{\\parshapeindent} primitive@> +primitive("parshapedimen", last_item, par_shape_dimen_code); +@!@:par\_shape\_dimen\_}{\.{\\parshapedimen} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case par_shape_length_code: print_esc("parshapelength");@+break; +case par_shape_indent_code: print_esc("parshapeindent");@+break; +case par_shape_dimen_code: print_esc("parshapedimen");@+break; + +@ @<Cases for fetching a dimension value@>= +case par_shape_length_code: +case par_shape_indent_code: +case par_shape_dimen_code: {@+q=cur_chr-par_shape_length_code;scan_int(); + if ((par_shape_ptr==null)||(cur_val <= 0)) cur_val=0; + else{@+if (q==2) + {@+q=cur_val%2;cur_val=(cur_val+q)/2; + } + if (cur_val > info(par_shape_ptr)) cur_val=info(par_shape_ptr); + cur_val=mem[par_shape_ptr+2*cur_val-q].sc; + } + cur_val_level=dimen_val; + } @+break; + +@ The \.{\\showgroups} command displays all currently active grouping +levels. + +@d show_groups 4 /* \.{\\showgroups} */ + +@<Generate all \eTeX...@>= +primitive("showgroups", xray, show_groups); +@!@:show\_groups\_}{\.{\\showgroups} primitive@> + +@ @<Cases of |xray| for |print_cmd_chr|@>= +case show_groups: print_esc("showgroups");@+break; + +@ @<Cases for |show_whatever|@>= +case show_groups: {@+begin_diagnostic();show_save_groups(); + } @+break; + +@ @<Types...@>= +typedef int32_t save_pointer; /*index into |save_stack|*/ + +@ The modifications of \TeX\ required for the display produced by the +|show_save_groups| procedure were first discussed by Donald~E. Knuth in +{\sl TUGboat\/} {\bf 11}, 165--170 and 499--511, 1990. +@^Knuth, Donald Ervin@> + +In order to understand a group type we also have to know its mode. +Since unrestricted horizontal modes are not associated with grouping, +they are skipped when traversing the semantic nest. + +@<Declare \eTeX\ procedures for use...@>= +static void show_save_groups(void) +{@+ +int p; /*index into |nest|*/ +int @!m; /*mode*/ +save_pointer @!v; /*saved value of |save_ptr|*/ +quarterword @!l; /*saved value of |cur_level|*/ +group_code @!c; /*saved value of |cur_group|*/ +int @!a; /*to keep track of alignments*/ +int @!i; +quarterword @!j; +char *@!s; +p=nest_ptr;nest[p]=cur_list; /*put the top level into the array*/ +v=save_ptr;l=cur_level;c=cur_group; +save_ptr=cur_boundary;decr(cur_level);@/ +a=1; +print_nl("");print_ln(); +loop@+{@+print_nl("### ");print_group(true); + if (cur_group==bottom_level) goto done; + @/do@+{m=nest[p].mode_field; + if (p > 0) decr(p);else m=vmode; + }@+ while (!(m!=hmode)); + print(" ("); + switch (cur_group) { + case simple_group: {@+incr(p);goto found2; + } + case hbox_group: case adjusted_hbox_group: s="hbox";@+break; + case vbox_group: s="vbox";@+break; + case vtop_group: s="vtop";@+break; + case align_group: if (a==0) + {@+if (m==-vmode) s="halign";else s="valign"; + a=1;goto found1; + } + else{@+if (a==1) print("align entry");else print_esc("cr"); + if (p >= a) p=p-a; + a=0;goto found; + } @+break; + case no_align_group: + {@+incr(p);a=-1;print_esc("noalign");goto found2; + } + case output_group: + {@+print_esc("output");goto found; + } + case math_group: goto found2; + case disc_group: case math_choice_group: + {@+if (cur_group==disc_group) print_esc("discretionary"); + else print_esc("mathchoice"); + for (i=1; i<=3; i++) if (i <= saved(-2)) print("{}"); + goto found2; + } + case insert_group: + {@+if (saved(-2)==255) print_esc("vadjust"); + else{@+print_esc("insert");print_int(saved(-2)); + } + goto found2; + } + case vcenter_group: {@+s="vcenter";goto found1; + } + case semi_simple_group: {@+incr(p);print_esc("begingroup");goto found; + } + case math_shift_group: + {@+if (m==mmode) print_char('$'); + else if (nest[p].mode_field==mmode) + {@+print_cmd_chr(eq_no, saved(-2));goto found; + } + print_char('$');goto found; + } + case math_left_group: + {@+if (type(nest[p+1].eTeX_aux_field)==left_noad) print_esc("left"); + else print_esc("middle"); + goto found; + } + } /*there are no other cases*/ + @<Show the box context@>; + found1: print_esc(s);@<Show the box packaging info@>; + found2: print_char('{'); + found: print_char(')');decr(cur_level); + cur_group=save_level(save_ptr);save_ptr=save_index(save_ptr); + } +done: save_ptr=v;cur_level=l;cur_group=c; +} + +@ @<Show the box packaging info@>= +if (saved(-2)!=0) + {@+print_char(' '); + if (saved(-3)==exactly) print("to");else print("spread"); + print_scaled(saved(-2));print("pt"); + } + +@ @<Show the box context@>= +i=saved(-4); +if (i!=0) + if (i < box_flag) + {@+if (abs(nest[p].mode_field)==vmode) j=hmove;else j=vmove; + if (i > 0) print_cmd_chr(j, 0);else print_cmd_chr(j, 1); + print_scaled(abs(i));print("pt"); + } + else if (i < ship_out_flag) + {@+if (i >= global_box_flag) + {@+print_esc("global");i=i-(global_box_flag-box_flag); + } + print_esc("setbox");print_int(i-box_flag);print_char('='); + } + else print_cmd_chr(leader_ship, i-(leader_flag-a_leaders)) + +@ The |scan_general_text| procedure is much like |scan_toks(false, false)|, +but will be invoked via |expand|, i.e., recursively. +@^recursion@> + +@<Declare \eTeX\ procedures for sc...@>= +static void scan_general_text(void); + +@ The token list (balanced text) created by |scan_general_text| begins +at |link(temp_head)| and ends at |cur_val|. (If |cur_val==temp_head|, +the list is empty.) + +@<Declare \eTeX\ procedures for tok...@>= +static void scan_general_text(void) +{@+ +int s; /*to save |scanner_status|*/ +pointer @!w; /*to save |warning_index|*/ +pointer @!d; /*to save |def_ref|*/ +pointer @!p; /*tail of the token list being built*/ +pointer @!q; /*new node being added to the token list via |store_new_token|*/ +halfword @!unbalance; /*number of unmatched left braces*/ +s=scanner_status;w=warning_index;d=def_ref; +scanner_status=absorbing;warning_index=cur_cs; +def_ref=get_avail();token_ref_count(def_ref)=null;p=def_ref; +scan_left_brace(); /*remove the compulsory left brace*/ +unbalance=1; +loop@+{@+get_token(); + if (cur_tok < right_brace_limit) + if (cur_cmd < right_brace) incr(unbalance); + else{@+decr(unbalance); + if (unbalance==0) goto found; + } + store_new_token(cur_tok); + } +found: q=link(def_ref);free_avail(def_ref); /*discard reference count*/ +if (q==null) cur_val=temp_head;@+else cur_val=p; +link(temp_head)=q; +scanner_status=s;warning_index=w;def_ref=d; +} + +@ The \.{\\showtokens} command displays a token list. + +@d show_tokens 5 /* \.{\\showtokens} , must be odd! */ + +@<Generate all \eTeX...@>= +primitive("showtokens", xray, show_tokens); +@!@:show\_tokens\_}{\.{\\showtokens} primitive@> + +@ @<Cases of |xray| for |print_cmd_chr|@>= +case show_tokens: print_esc("showtokens");@+break; + +@ The \.{\\unexpanded} primitive prevents expansion of tokens much as +the result from \.{\\the} applied to a token variable. The +\.{\\detokenize} primitive converts a token list into a list of +character tokens much as if the token list were written to a file. We +use the fact that the command modifiers for \.{\\unexpanded} and +\.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe} +are even. + +@<Generate all \eTeX...@>= +primitive("unexpanded", the, 1);@/ +@!@:unexpanded\_}{\.{\\unexpanded} primitive@> +primitive("detokenize", the, show_tokens);@/ +@!@:detokenize\_}{\.{\\detokenize} primitive@> + +@ @<Cases of |the| for |print_cmd_chr|@>=; +else if (chr_code==1) print_esc("unexpanded"); +else print_esc("detokenize") + +@ @<Handle \.{\\unexpanded} or \.{\\detokenize} and |return|@>= +if (odd(cur_chr)) + {@+c=cur_chr;scan_general_text(); + if (c==1) return cur_val; + else{@+old_setting=selector;selector=new_string;b=pool_ptr; + p=get_avail();link(p)=link(temp_head); + token_show(p);flush_list(p); + selector=old_setting;return str_toks(b); + } + + } + +@ The \.{\\showifs} command displays all currently active conditionals. + +@d show_ifs 6 /* \.{\\showifs} */ + +@<Generate all \eTeX...@>= +primitive("showifs", xray, show_ifs); +@!@:show\_ifs\_}{\.{\\showifs} primitive@> + +@ @<Cases of |xray| for |print_cmd_chr|@>= +case show_ifs: print_esc("showifs");@+break; + +@ @d print_if_line(A) if (A!=0) + {@+print(" entered on line ");print_int(A); + } + +@<Cases for |show_whatever|@>= +case show_ifs: {@+begin_diagnostic();print_nl("");print_ln(); + if (cond_ptr==null) + {@+print_nl("### ");print("no active conditionals"); + } + else{@+p=cond_ptr;n=0; + @/do@+{incr(n);p=link(p);@+}@+ while (!(p==null)); + p=cond_ptr;t=cur_if;l=if_line;m=if_limit; + @/do@+{print_nl("### level ");print_int(n);print(": "); + print_cmd_chr(if_test, t); + if (m==fi_code) print_esc("else"); + print_if_line(l); + decr(n);t=subtype(p);l=if_line_field(p);m=type(p);p=link(p); + }@+ while (!(p==null)); + } + } @+break; + +@ The \.{\\interactionmode} primitive allows to query and set the +interaction mode. + +@<Generate all \eTeX...@>= +primitive("interactionmode", set_page_int, 2); +@!@:interaction\_mode\_}{\.{\\interactionmode} primitive@> + +@ @<Cases of |set_page_int| for |print_cmd_chr|@>=; +else if (chr_code==2) print_esc("interactionmode") + +@ @<Cases for `Fetch the |dead_cycles| or the |insert_penalties|'@>=; +else if (m==2) cur_val=interaction + +@ @<Declare \eTeX\ procedures for use...@>= +static void new_interaction(void); + +@ @<Cases for |alter_integer|@>=; +else if (c==2) + {@+if ((cur_val < batch_mode)||(cur_val > error_stop_mode)) + {@+print_err("Bad interaction mode"); +@.Bad interaction mode@> + help2("Modes are 0=batch, 1=nonstop, 2=scroll, and",@/ + "3=errorstop. Proceed, and I'll ignore this case."); + int_error(cur_val); + } + else{@+cur_chr=cur_val;new_interaction(); + } + } + +@ The |middle| feature of \eTeX\ allows one ore several \.{\\middle} +delimiters to appear between \.{\\left} and \.{\\right}. + +@<Generate all \eTeX...@>= +primitive("middle", left_right, middle_noad); +@!@:middle\_}{\.{\\middle} primitive@> + +@ @<Cases of |left_right| for |print_cmd_chr|@>=; +else if (chr_code==middle_noad) print_esc("middle") + +@ The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} +primitive. + +@<Generate all \eTeX...@>= +primitive("scantokens", input, 2); +@!@:scan\_tokens\_}{\.{\\scantokens} primitive@> + +@ @<Cases of |input| for |print_cmd_chr|@>=; +else if (chr_code==2) print_esc("scantokens") + +@ @<Cases for |input|@>=; +else if (cur_chr==2) pseudo_start() + +@ The global variable |pseudo_files| is used to maintain a stack of +pseudo files. The |info| field of each pseudo file points to a linked +list of variable size nodes representing lines not yet processed: the +|info| field of the first word contains the size of this node, all the +following words contain ASCII codes. + +@<Glob...@>= +static pointer @!pseudo_files; /*stack of pseudo files*/ + +@ @<Set init...@>= +pseudo_files=null; + +@ The |pseudo_start| procedure initiates reading from a pseudo file. + +@<Declare \eTeX\ procedures for ex...@>= +static void pseudo_start(void); + +@ @<Declare \eTeX\ procedures for tok...@>= +static void pseudo_start(void) +{@+int old_setting; /*holds |selector| setting*/ +str_number @!s; /*string to be converted into a pseudo file*/ +pool_pointer @!l, @!m; /*indices into |str_pool|*/ +pointer @!p, @!q, @!r; /*for list construction*/ +four_quarters @!w; /*four ASCII codes*/ +int @!nl, @!sz; +scan_general_text(); +old_setting=selector;selector=new_string; +token_show(temp_head);selector=old_setting; +flush_list(link(temp_head)); +str_room(1);s=make_string(); +@<Convert string |s| into a new pseudo file@>; +flush_string; +@<Initiate input from new pseudo file@>; +} + +@ @<Convert string |s| into a new pseudo file@>= +str_pool[pool_ptr]=si(' ');l=str_start[s]; +nl=si(new_line_char); +p=get_avail();q=p; +while (l < pool_ptr) + {@+m=l; + while ((l < pool_ptr)&&(str_pool[l]!=nl)) incr(l); + sz=(l-m+7)/4; + if (sz==1) sz=2; + r=get_node(sz);link(q)=r;q=r;info(q)=hi(sz); + while (sz > 2) + {@+decr(sz);incr(r); + w.b0=qi(so(str_pool[m]));w.b1=qi(so(str_pool[m+1])); + w.b2=qi(so(str_pool[m+2]));w.b3=qi(so(str_pool[m+3])); + mem[r].qqqq=w;m=m+4; + } + w.b0=qi(' ');w.b1=qi(' ');w.b2=qi(' ');w.b3=qi(' '); + if (l > m) + {@+w.b0=qi(so(str_pool[m])); + if (l > m+1) + {@+w.b1=qi(so(str_pool[m+1])); + if (l > m+2) + {@+w.b2=qi(so(str_pool[m+2])); + if (l > m+3) w.b3=qi(so(str_pool[m+3])); + } + } + } + mem[r+1].qqqq=w; + if (str_pool[l]==nl) incr(l); + } +info(p)=link(p);link(p)=pseudo_files;pseudo_files=p + +@ @<Initiate input from new pseudo file@>= +begin_file_reading(); /*set up |cur_file| and new level of input*/ +line=0;limit=start;loc=limit+1; /*force line read*/ +if (tracing_scan_tokens > 0) + {@+if (term_offset > max_print_line-3) print_ln(); + else if ((term_offset > 0)||(file_offset > 0)) print_char(' '); + name=19;print("( ");incr(open_parens);update_terminal; + } +else name=18 + +@ Here we read a line from the current pseudo file into |buffer|. + +@<Declare \eTeX\ procedures for tr...@>= +static bool pseudo_input(void) /*inputs the next line or returns |false|*/ +{@+pointer p; /*current line from pseudo file*/ +int @!sz; /*size of node |p|*/ +four_quarters @!w; /*four ASCII codes*/ +int @!r; /*loop index*/ +last=first; /*cf.\ Matthew 19\thinspace:\thinspace30*/ +p=info(pseudo_files); +if (p==null) return false; +else{@+info(pseudo_files)=link(p);sz=ho(info(p)); + if (4*sz-3 >= buf_size-last) + @<Report overflow of the input buffer, and abort@>; + last=first; + for (r=p+1; r<=p+sz-1; r++) + {@+w=mem[r].qqqq; + buffer[last]=w.b0;buffer[last+1]=w.b1; + buffer[last+2]=w.b2;buffer[last+3]=w.b3; + last=last+4; + } + if (last >= max_buf_stack) max_buf_stack=last+1; + while ((last > first)&&(buffer[last-1]==' ')) decr(last); + free_node(p, sz); + return true; + } +} + +@ When we are done with a pseudo file we `close' it. + +@<Declare \eTeX\ procedures for tr...@>= +static void pseudo_close(void) /*close the top level pseudo file*/ +{@+pointer p, @!q; +p=link(pseudo_files);q=info(pseudo_files); +free_avail(pseudo_files);pseudo_files=p; +while (q!=null) + {@+p=q;q=link(p);free_node(p, ho(info(p))); + } +} + +@ @<Dump the \eTeX\ state@>= +while (pseudo_files!=null) pseudo_close(); /*flush pseudo files*/ + +@ @<Generate all \eTeX...@>= +primitive("readline", read_to_cs, 1);@/ +@!@:read\_line\_}{\.{\\readline} primitive@> + +@ @<Cases of |read| for |print_cmd_chr|@>=; +else print_esc("readline") + +@ @<Handle \.{\\readline} and |goto done|@>= +if (j==1) + {@+while (loc <= limit) /*current line not yet finished*/ + {@+cur_chr=buffer[loc];incr(loc); + if (cur_chr==' ') cur_tok=space_token; + @+else cur_tok=cur_chr+other_token; + store_new_token(cur_tok); + } + goto done; + } + +@ Here we define the additional conditionals of \eTeX\ as well as the +\.{\\unless} prefix. + +@d if_def_code 17 /* `\.{\\ifdefined}' */ +@d if_cs_code 18 /* `\.{\\ifcsname}' */ +@d if_font_char_code 19 /* `\.{\\iffontchar}' */ +@d eTeX_last_if_test_cmd_mod if_font_char_code +@d eTeX_last_expand_after_cmd_mod 1 + +@<Generate all \eTeX...@>= +primitive("unless", expand_after, 1);@/ +@!@:unless\_}{\.{\\unless} primitive@> +primitive("ifdefined", if_test, if_def_code); +@!@:if\_defined\_}{\.{\\ifdefined} primitive@> +primitive("ifcsname", if_test, if_cs_code); +@!@:if\_cs\_name\_}{\.{\\ifcsname} primitive@> +primitive("iffontchar", if_test, if_font_char_code); +@!@:if\_font\_char\_}{\.{\\iffontchar} primitive@> + +@ @<Cases of |expandafter| for |print_cmd_chr|@>= +case 1: print_esc("unless");@+break; + +@ @<Cases of |if_test| for |print_cmd_chr|@>= +case if_def_code: print_esc("ifdefined");@+break; +case if_cs_code: print_esc("ifcsname");@+break; +case if_font_char_code: print_esc("iffontchar");@+break; + +@ The result of a boolean condition is reversed when the conditional is +preceded by \.{\\unless}. + +@<Negate a boolean conditional and |goto reswitch|@>= +{@+get_token(); +if ((cur_cmd==if_test)&&(cur_chr!=if_case_code)) + {@+cur_chr=cur_chr+unless_code;goto reswitch; + } +print_err("You can't use `");print_esc("unless");print("' before `"); +@.You can't use \\unless...@> +print_cmd_chr(cur_cmd, cur_chr);print_char('\''); +help1("Continue, and I'll forget that it ever happened."); +back_error(); +} + +@ The conditional \.{\\ifdefined} tests if a control sequence is +defined. + +We need to reset |scanner_status|, since \.{\\outer} control sequences +are allowed, but we might be scanning a macro definition or preamble. + +@<Cases for |conditional|@>= +case if_def_code: {@+save_scanner_status=scanner_status; + scanner_status=normal; + get_next();b=(cur_cmd!=undefined_cs); + scanner_status=save_scanner_status; + } @+break; + +@ The conditional \.{\\ifcsname} is equivalent to \.{\{\\expandafter} +\.{\}\\expandafter} \.{\\ifdefined} \.{\\csname}, except that no new +control sequence will be entered into the hash table (once all tokens +preceding the mandatory \.{\\endcsname} have been expanded). + +@<Cases for |conditional|@>= +case if_cs_code: {@+n=get_avail();p=n; /*head of the list of characters*/ + @/do@+{get_x_token(); + if (cur_cs==0) store_new_token(cur_tok); + }@+ while (!(cur_cs!=0)); + if (cur_cmd!=end_cs_name) @<Complain about missing \.{\\endcsname}@>; + @<Look up the characters of list |n| in the hash table, and set |cur_cs|@>; + flush_list(n); + b=(eq_type(cur_cs)!=undefined_cs); + } @+break; + +@ @<Look up the characters of list |n| in the hash table...@>= +m=first;p=link(n); +while (p!=null) + {@+if (m >= max_buf_stack) + {@+max_buf_stack=m+1; + if (max_buf_stack==buf_size) + overflow("buffer size", buf_size); +@:TeX capacity exceeded buffer size}{\quad buffer size@> + } + buffer[m]=info(p)%0400;incr(m);p=link(p); + } +if (m==first) cur_cs=null_cs; /*the list is empty*/ +else if (m > first+1) + cur_cs=id_lookup(first, m-first); /*|no_new_control_sequence| is |true|*/ +else cur_cs=single_base+buffer[first] /*the list has length one*/ + +@ The conditional \.{\\iffontchar} tests the existence of a character in +a font. + +@<Cases for |conditional|@>= +case if_font_char_code: {@+scan_font_ident();n=cur_val;scan_char_num(); + if ((font_bc[n] <= cur_val)&&(font_ec[n] >= cur_val)) + b=char_exists(char_info(n, qi(cur_val))); + else b=false; + } @+break; + +@ The \.{protected} feature of \eTeX\ defines the \.{\\protected} prefix +command for macro definitions. Such macros are protected against +expansions when lists of expanded tokens are built, e.g., for \.{\\edef} +or during \.{\\write}. + +@<Generate all \eTeX...@>= +primitive("protected", prefix, 8); +@!@:protected\_}{\.{\\protected} primitive@> + +@ @<Cases of |prefix| for |print_cmd_chr|@>=; +else if (chr_code==8) print_esc("protected") + +@ The |get_x_or_protected| procedure is like |get_x_token| except that +protected macros are not expanded. + +@<Declare \eTeX\ procedures for sc...@>= +static void get_x_or_protected(void) /*sets |cur_cmd|, |cur_chr|, |cur_tok|, + and expands non-protected macros*/ +{@+ +loop@+{@+get_token(); + if (cur_cmd <= max_command) return; + if ((cur_cmd >= call)&&(cur_cmd < end_template)) + if (info(link(cur_chr))==protected_token) return; + expand(); + } +} + +@ A group entered (or a conditional started) in one file may end in a +different file. Such slight anomalies, although perfectly legitimate, +may cause errors that are difficult to locate. In order to be able to +give a warning message when such anomalies occur, \eTeX\ uses the +|grp_stack| and |if_stack| arrays to record the initial |cur_boundary| +and |cond_ptr| values for each input file. + +@<Glob...@>= +static save_pointer @!grp_stack[max_in_open+1]; /*initial |cur_boundary|*/ +static pointer @!if_stack[max_in_open+1]; /*initial |cond_ptr|*/ + +@ When a group ends that was apparently entered in a different input +file, the |group_warning| procedure is invoked in order to update the +|grp_stack|. If moreover \.{\\tracingnesting} is positive we want to +give a warning message. The situation is, however, somewhat complicated +by two facts: (1)~There may be |grp_stack| elements without a +corresponding \.{\\input} file or \.{\\scantokens} pseudo file (e.g., +error insertions from the terminal); and (2)~the relevant information is +recorded in the |name_field| of the |input_stack| only loosely +synchronized with the |in_open| variable indexing |grp_stack|. + +@<Declare \eTeX\ procedures for tr...@>= +static void group_warning(void) +{@+int i; /*index into |grp_stack|*/ +bool @!w; /*do we need a warning?*/ +base_ptr=input_ptr;input_stack[base_ptr]=cur_input; + /*store current state*/ +i=in_open;w=false; +while ((grp_stack[i]==cur_boundary)&&(i > 0)) + {@+@<Set variable |w| to indicate if this case should be reported@>; + grp_stack[i]=save_index(save_ptr);decr(i); + } +if (w) + {@+print_nl("Warning: end of ");print_group(true); +@.Warning: end of...@> + print(" of a different file");print_ln(); + if (tracing_nesting > 1) show_context(); + if (history==spotless) history=warning_issued; + } +} + +@ This code scans the input stack in order to determine the type of the +current input file. + +@<Set variable |w| to...@>= +if (tracing_nesting > 0) + {@+while ((input_stack[base_ptr].state_field==token_list)||@| + (input_stack[base_ptr].index_field > i)) decr(base_ptr); + if (input_stack[base_ptr].name_field > 17) w=true; + } + +@ When a conditional ends that was apparently started in a different +input file, the |if_warning| procedure is invoked in order to update the +|if_stack|. If moreover \.{\\tracingnesting} is positive we want to +give a warning message (with the same complications as above). + +@<Declare \eTeX\ procedures for tr...@>= +static void if_warning(void) +{@+int i; /*index into |if_stack|*/ +bool @!w; /*do we need a warning?*/ +base_ptr=input_ptr;input_stack[base_ptr]=cur_input; + /*store current state*/ +i=in_open;w=false; +while (if_stack[i]==cond_ptr) + {@+@<Set variable |w| to...@>; + if_stack[i]=link(cond_ptr);decr(i); + } +if (w) + {@+print_nl("Warning: end of ");print_cmd_chr(if_test, cur_if); +@.Warning: end of...@> + print_if_line(if_line);print(" of a different file");print_ln(); + if (tracing_nesting > 1) show_context(); + if (history==spotless) history=warning_issued; + } +} + +@ Conversely, the |file_warning| procedure is invoked when a file ends +and some groups entered or conditionals started while reading from that +file are still incomplete. + +@<Declare \eTeX\ procedures for tr...@>= +static void file_warning(void) +{@+pointer p; /*saved value of |save_ptr| or |cond_ptr|*/ +quarterword @!l; /*saved value of |cur_level| or |if_limit|*/ +quarterword @!c; /*saved value of |cur_group| or |cur_if|*/ +int @!i; /*saved value of |if_line|*/ +p=save_ptr;l=cur_level;c=cur_group;save_ptr=cur_boundary; +while (grp_stack[in_open]!=save_ptr) + {@+decr(cur_level); + print_nl("Warning: end of file when "); +@.Warning: end of file when...@> + print_group(true);print(" is incomplete");@/ + cur_group=save_level(save_ptr);save_ptr=save_index(save_ptr); + } +save_ptr=p;cur_level=l;cur_group=c; /*restore old values*/ +p=cond_ptr;l=if_limit;c=cur_if;i=if_line; +while (if_stack[in_open]!=cond_ptr) + {@+print_nl("Warning: end of file when "); +@.Warning: end of file when...@> + print_cmd_chr(if_test, cur_if); + if (if_limit==fi_code) print_esc("else"); + print_if_line(if_line);print(" is incomplete");@/ + if_line=if_line_field(cond_ptr);cur_if=subtype(cond_ptr); + if_limit=type(cond_ptr);cond_ptr=link(cond_ptr); + } +cond_ptr=p;if_limit=l;cur_if=c;if_line=i; /*restore old values*/ +print_ln(); +if (tracing_nesting > 1) show_context(); +if (history==spotless) history=warning_issued; +} + +@ Here are the additional \eTeX\ primitives for expressions. + +@<Generate all \eTeX...@>= +primitive("numexpr", last_item, eTeX_expr-int_val+int_val); +@!@:num\_expr\_}{\.{\\numexpr} primitive@> +primitive("dimexpr", last_item, eTeX_expr-int_val+dimen_val); +@!@:dim\_expr\_}{\.{\\dimexpr} primitive@> +primitive("glueexpr", last_item, eTeX_expr-int_val+glue_val); +@!@:glue\_expr\_}{\.{\\glueexpr} primitive@> +primitive("muexpr", last_item, eTeX_expr-int_val+mu_val); +@!@:mu\_expr\_}{\.{\\muexpr} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case eTeX_expr-int_val+int_val: print_esc("numexpr");@+break; +case eTeX_expr-int_val+dimen_val: print_esc("dimexpr");@+break; +case eTeX_expr-int_val+glue_val: print_esc("glueexpr");@+break; +case eTeX_expr-int_val+mu_val: print_esc("muexpr");@+break; + +@ This code for reducing |cur_val_level| and\slash or negating the +result is similar to the one for all the other cases of +|scan_something_internal|, with the difference that |scan_expr| has +already increased the reference count of a glue specification. + +@<Process an expression and |return|@>= +{@+if (m < eTeX_mu) + {@+switch (m) { + @/@<Cases for fetching a glue value@>@/ + } /*there are no other cases*/ + cur_val_level=glue_val; + } +else if (m < eTeX_expr) + {@+switch (m) { + @/@<Cases for fetching a mu value@>@/ + } /*there are no other cases*/ + cur_val_level=mu_val; + } +else{@+cur_val_level=m-eTeX_expr+int_val;scan_expr(); + } +while (cur_val_level > level) + {@+if (cur_val_level==glue_val) + {@+m=cur_val;cur_val=width(m);delete_glue_ref(m); + } + else if (cur_val_level==mu_val) mu_error(); + decr(cur_val_level); + } +if (negative) + if (cur_val_level >= glue_val) + {@+m=cur_val;cur_val=new_spec(m);delete_glue_ref(m); + @<Negate all three glue components of |cur_val|@>; + } + else negate(cur_val); +return; +} + +@ @<Declare \eTeX\ procedures for sc...@>= +static void scan_expr(void); + +@ The |scan_expr| procedure scans and evaluates an expression. + +@<Declare procedures needed for expressions@>= +@t\4@>@<Declare subprocedures for |scan_expr|@>@; +static void scan_expr(void) /*scans and evaluates an expression*/ +{@+ +bool a, @!b; /*saved values of |arith_error|*/ +small_number @!l; /*type of expression*/ +small_number @!r; /*state of expression so far*/ +small_number @!s; /*state of term so far*/ +small_number @!o; /*next operation or type of next factor*/ +int @!e; /*expression so far*/ +int @!t; /*term so far*/ +int @!f; /*current factor*/ +int @!n; /*numerator of combined multiplication and division*/ +pointer @!p; /*top of expression stack*/ +pointer @!q; /*for stack manipulations*/ +l=cur_val_level;a=arith_error;b=false;p=null; +@<Scan and evaluate an expression |e| of type |l|@>; +if (b) + {@+print_err("Arithmetic overflow"); +@.Arithmetic overflow@> + help2("I can't evaluate this expression,",@/ + "since the result is out of range."); + error(); + if (l >= glue_val) + {@+delete_glue_ref(e);e=zero_glue;add_glue_ref(e); + } + else e=0; + } +arith_error=a;cur_val=e;cur_val_level=l; +} + +@ Evaluating an expression is a recursive process: When the left +parenthesis of a subexpression is scanned we descend to the next level +of recursion; the previous level is resumed with the matching right +parenthesis. + +@d expr_none 0 /*\.( seen, or \.( $\langle\it expr\rangle$ \.) seen*/ +@d expr_add 1 /*\.( $\langle\it expr\rangle$ \.+ seen*/ +@d expr_sub 2 /*\.( $\langle\it expr\rangle$ \.- seen*/ +@d expr_mult 3 /*$\langle\it term\rangle$ \.* seen*/ +@d expr_div 4 /*$\langle\it term\rangle$ \./ seen*/ +@d expr_scale 5 /*$\langle\it term\rangle$ \.* + $\langle\it factor\rangle$ \./ seen*/ + +@<Scan and eval...@>= +restart: r=expr_none;e=0;s=expr_none;t=0;n=0; +resume: if (s==expr_none) o=l;@+else o=int_val; +@<Scan a factor |f| of type |o| or start a subexpression@>; +found: @<Scan the next operator and set |o|@>; +arith_error=b; +@<Make sure that |f| is in the proper range@>; +switch (s) {@<Cases for evaluation of the current term@>@; +} /*there are no other cases*/ +if (o > expr_sub) s=o;@+else@<Evaluate the current expression@>; +b=arith_error; +if (o!=expr_none) goto resume; +if (p!=null) @<Pop the expression stack and |goto found|@>@; + +@ @<Scan the next op...@>= +@<Get the next non-blank non-call token@>; +if (cur_tok==other_token+'+') o=expr_add; +else if (cur_tok==other_token+'-') o=expr_sub; +else if (cur_tok==other_token+'*') o=expr_mult; +else if (cur_tok==other_token+'/') o=expr_div; +else{@+o=expr_none; + if (p==null) + {@+if (cur_cmd!=relax) back_input(); + } + else if (cur_tok!=other_token+')') + {@+print_err("Missing ) inserted for expression"); +@.Missing ) inserted@> + help1("I was expecting to see `+', `-', `*', `/', or `)'. Didn't."); + back_error(); + } + } + +@ @<Scan a factor...@>= +@<Get the next non-blank non-call token@>; +if (cur_tok==other_token+'(') + @<Push the expression stack and |goto restart|@>; +back_input(); +if (o==int_val) scan_int(); +else if (o==dimen_val) scan_normal_dimen; +else if (o==glue_val) scan_normal_glue(); +else scan_mu_glue(); +f=cur_val + +@ @<Declare \eTeX\ procedures for sc...@>= +static void scan_normal_glue(void);@/ +static void scan_mu_glue(void); + +@ Here we declare two trivial procedures in order to avoid mutually +recursive procedures with parameters. + +@<Declare procedures needed for expressions@>= +static void scan_normal_glue(void) +{@+scan_glue(glue_val); +} +@# +static void scan_mu_glue(void) +{@+scan_glue(mu_val); +} + +@ Parenthesized subexpressions can be inside expressions, and this +nesting has a stack. Seven local variables represent the top of the +expression stack: |p| points to pushed-down entries, if any; |l| +specifies the type of expression currently beeing evaluated; |e| is the +expression so far and |r| is the state of its evaluation; |t| is the +term so far and |s| is the state of its evaluation; finally |n| is the +numerator for a combined multiplication and division, if any. + +@d expr_node_size 4 /*number of words in stack entry for subexpressions*/ +@d expr_e_field(A) mem[A+1].i /*saved expression so far*/ +@d expr_t_field(A) mem[A+2].i /*saved term so far*/ +@d expr_n_field(A) mem[A+3].i /*saved numerator*/ + +@<Push the expression...@>= +{@+q=get_node(expr_node_size);link(q)=p;type(q)=l; +subtype(q)=4*s+r; +expr_e_field(q)=e;expr_t_field(q)=t;expr_n_field(q)=n; +p=q;l=o;goto restart; +} + +@ @<Pop the expression...@>= +{@+f=e;q=p; +e=expr_e_field(q);t=expr_t_field(q);n=expr_n_field(q); +s=subtype(q)/4;r=subtype(q)%4; +l=type(q);p=link(q);free_node(q, expr_node_size); +goto found; +} + +@ We want to make sure that each term and (intermediate) result is in +the proper range. Integer values must not exceed |infinity| +($2^{31}-1$) in absolute value, dimensions must not exceed |max_dimen| +($2^{30}-1$). We avoid the absolute value of an integer, because this +might fail for the value $-2^{31}$ using 32-bit arithmetic. + +@d num_error(A) /*clear a number or dimension and set |arith_error|*/ + {@+arith_error=true;A=0; + } +@d glue_error(A) /*clear a glue spec and set |arith_error|*/ + {@+arith_error=true;delete_glue_ref(A);A=new_spec(zero_glue); + } + +@<Make sure that |f|...@>= +if ((l==int_val)||(s > expr_sub)) + {@+if ((f > infinity)||(f < -infinity)) num_error(f); + } +else if (l==dimen_val) + {@+if (abs(f) > max_dimen) num_error(f); + } +else{@+if ((abs(width(f)) > max_dimen)||@| + (abs(stretch(f)) > max_dimen)||@| + (abs(shrink(f)) > max_dimen)) glue_error(f); + } + +@ Applying the factor |f| to the partial term |t| (with the operator +|s|) is delayed until the next operator |o| has been scanned. Here we +handle the first factor of a partial term. A glue spec has to be copied +unless the next operator is a right parenthesis; this allows us later on +to simply modify the glue components. + +@d normalize_glue(A) + if (stretch(A)==0) stretch_order(A)=normal; + if (shrink(A)==0) shrink_order(A)=normal + +@<Cases for evaluation of the current term@>= +case expr_none: if ((l >= glue_val)&&(o!=expr_none)) + {@+t=new_spec(f);delete_glue_ref(f);normalize_glue(t); + } + else t=f;@+break; + +@ When a term |t| has been completed it is copied to, added to, or +subtracted from the expression |e|. + +@d expr_add_sub(A, B, C) add_or_sub(A, B, C, r==expr_sub) +@d expr_a(A, B) expr_add_sub(A, B, max_dimen) + +@<Evaluate the current expression@>= +{@+s=expr_none; +if (r==expr_none) e=t; +else if (l==int_val) e=expr_add_sub(e, t, infinity); +else if (l==dimen_val) e=expr_a(e, t); +else@<Compute the sum or difference of two glue specs@>; +r=o; +} + +@ The function |add_or_sub(x, y, max_answer, negative)| computes the sum +(for |negative==false|) or difference (for |negative==true|) of |x| and +|y|, provided the absolute value of the result does not exceed +|max_answer|. + +@<Declare subprocedures for |scan_expr|@>= +static int add_or_sub(int @!x, int @!y, int @!max_answer, bool @!negative) +{@+int a; /*the answer*/ +if (negative) negate(y); +if (x >= 0) + if (y <= max_answer-x) a=x+y;@+else num_error(a)@; +else if (y >= -max_answer-x) a=x+y;@+else num_error(a); +return a; +} + +@ We know that |stretch_order(e) > normal| implies |stretch(e)!=0| and +|shrink_order(e) > normal| implies |shrink(e)!=0|. + +@<Compute the sum or diff...@>= +{@+width(e)=expr_a(width(e), width(t)); +if (stretch_order(e)==stretch_order(t)) + stretch(e)=expr_a(stretch(e), stretch(t)); +else if ((stretch_order(e) < stretch_order(t))&&(stretch(t)!=0)) + {@+stretch(e)=stretch(t);stretch_order(e)=stretch_order(t); + } +if (shrink_order(e)==shrink_order(t)) + shrink(e)=expr_a(shrink(e), shrink(t)); +else if ((shrink_order(e) < shrink_order(t))&&(shrink(t)!=0)) + {@+shrink(e)=shrink(t);shrink_order(e)=shrink_order(t); + } +delete_glue_ref(t);normalize_glue(e); +} + +@ If a multiplication is followed by a division, the two operations are +combined into a `scaling' operation. Otherwise the term |t| is +multiplied by the factor |f|. + +@d expr_m(A) A=nx_plus_y(A, f, 0) + +@<Cases for evaluation of the current term@>= +case expr_mult: if (o==expr_div) + {@+n=f;o=expr_scale; + } + else if (l==int_val) t=mult_integers(t, f); + else if (l==dimen_val) expr_m(t); + else{@+expr_m(width(t));expr_m(stretch(t));expr_m(shrink(t)); + } @+break; + +@ Here we divide the term |t| by the factor |f|. + +@d expr_d(A) A=quotient(A, f) + +@<Cases for evaluation of the current term@>= +case expr_div: if (l < glue_val) expr_d(t); + else{@+expr_d(width(t));expr_d(stretch(t));expr_d(shrink(t)); + } @+break; + +@ The function |quotient(n, d)| computes the rounded quotient +$q=\lfloor n/d+{1\over2}\rfloor$, when $n$ and $d$ are positive. + +@<Declare subprocedures for |scan_expr|@>= +static int quotient(int @!n, int @!d) +{@+bool negative; /*should the answer be negated?*/ +int @!a; /*the answer*/ +if (d==0) num_error(a)@; +else{@+if (d > 0) negative=false; + else{@+negate(d);negative=true; + } + if (n < 0) + {@+negate(n);negative=!negative; + } + a=n/d;n=n-a*d;d=n-d; /*avoid certain compiler optimizations!*/ + if (d+n >= 0) incr(a); + if (negative) negate(a); + } +return a; +} + +@ Here the term |t| is multiplied by the quotient $n/f$. + +@d expr_s(A) A=fract(A, n, f, max_dimen) + +@<Cases for evaluation of the current term@>= +case expr_scale: if (l==int_val) t=fract(t, n, f, infinity); + else if (l==dimen_val) expr_s(t); + else{@+expr_s(width(t));expr_s(stretch(t));expr_s(shrink(t)); + } + +@ Finally, the function |fract(x, n, d, max_answer)| computes the integer +$q=\lfloor xn/d+{1\over2}\rfloor$, when $x$, $n$, and $d$ are positive +and the result does not exceed |max_answer|. We can't use floating +point arithmetic since the routine must produce identical results in all +cases; and it would be too dangerous to multiply by~|n| and then divide +by~|d|, in separate operations, since overflow might well occur. Hence +this subroutine simulates double precision arithmetic, somewhat +analogous to \MF's |make_fraction| and |take_fraction| routines. + +@<Declare subprocedures for |scan_expr|@>= +static int fract(int @!x, int @!n, int @!d, int @!max_answer) +{@+ +bool negative; /*should the answer be negated?*/ +int @!a; /*the answer*/ +int @!f; /*a proper fraction*/ +int @!h; /*smallest integer such that |2*h >= d|*/ +int @!r; /*intermediate remainder*/ +int @!t; /*temp variable*/ +if (d==0) goto too_big; +a=0; +if (d > 0) negative=false; +else{@+negate(d);negative=true; + } +if (x < 0) + {@+negate(x);negative=!negative; + } +else if (x==0) goto done; +if (n < 0) + {@+negate(n);negative=!negative; + } +t=n/d; +if (t > max_answer/x) goto too_big; +a=t*x;n=n-t*d; +if (n==0) goto found; +t=x/d; +if (t > (max_answer-a)/n) goto too_big; +a=a+t*n;x=x-t*d; +if (x==0) goto found; +if (x < n) + {@+t=x;x=n;n=t; + } /*now |0 < n <= x < d|*/ +@<Compute \(f)$f=\lfloor xn/d+{1\over2}\rfloor$@>@; +if (f > (max_answer-a)) goto too_big; +a=a+f; +found: if (negative) negate(a); +goto done; +too_big: num_error(a); +done: return a; +} + +@ The loop here preserves the following invariant relations +between |f|, |x|, |n|, and~|r|: +(i)~$f+\lfloor(xn+(r+d))/d\rfloor=\lfloor x_0n_0/d+{1\over2}\rfloor$; +(ii)~|-d <= r < 0 < n <= x < d|, where $x_0$, $n_0$ are the original values of~$x$ +and $n$. + +Notice that the computation specifies |(x-d)+x| instead of |(x+x)-d|, +because the latter could overflow. + +@<Compute \(f)$f=\lfloor xn/d+{1\over2}\rfloor$@>= +f=0;r=(d/2)-d;h=-r; +loop@+{@+if (odd(n)) + {@+r=r+x; + if (r >= 0) + {@+r=r-d;incr(f); + } + } + n=n/2; + if (n==0) goto found1; + if (x < h) x=x+x; + else{@+t=x-d;x=t+x;f=f+n; + if (x < n) + {@+if (x==0) goto found1; + t=x;x=n;n=t; + } + } + } +found1: + +@ The \.{\\gluestretch}, \.{\\glueshrink}, \.{\\gluestretchorder}, and +\.{\\glueshrinkorder} commands return the stretch and shrink components +and their orders of ``infinity'' of a glue specification. + +@d glue_stretch_order_code (eTeX_int+6) /*code for \.{\\gluestretchorder}*/ +@d glue_shrink_order_code (eTeX_int+7) /*code for \.{\\glueshrinkorder}*/ +@d glue_stretch_code (eTeX_dim+7) /*code for \.{\\gluestretch}*/ +@d glue_shrink_code (eTeX_dim+8) /*code for \.{\\glueshrink}*/ + +@<Generate all \eTeX...@>= +primitive("gluestretchorder", last_item, glue_stretch_order_code); +@!@:glue\_stretch\_order\_}{\.{\\gluestretchorder} primitive@> +primitive("glueshrinkorder", last_item, glue_shrink_order_code); +@!@:glue\_shrink\_order\_}{\.{\\glueshrinkorder} primitive@> +primitive("gluestretch", last_item, glue_stretch_code); +@!@:glue\_stretch\_}{\.{\\gluestretch} primitive@> +primitive("glueshrink", last_item, glue_shrink_code); +@!@:glue\_shrink\_}{\.{\\glueshrink} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case glue_stretch_order_code: print_esc("gluestretchorder");@+break; +case glue_shrink_order_code: print_esc("glueshrinkorder");@+break; +case glue_stretch_code: print_esc("gluestretch");@+break; +case glue_shrink_code: print_esc("glueshrink");@+break; + +@ @<Cases for fetching an integer value@>= +case glue_stretch_order_code: case glue_shrink_order_code: + {@+scan_normal_glue();q=cur_val; + if (m==glue_stretch_order_code) cur_val=stretch_order(q); + else cur_val=shrink_order(q); + delete_glue_ref(q); + } + +@ @<Cases for fetching a dimension value@>= +case glue_stretch_code: case glue_shrink_code: + {@+scan_normal_glue();q=cur_val; + if (m==glue_stretch_code) cur_val=stretch(q); + else cur_val=shrink(q); + delete_glue_ref(q); + } + +@ The \.{\\mutoglue} and \.{\\gluetomu} commands convert ``math'' glue +into normal glue and vice versa; they allow to manipulate math glue with +\.{\\gluestretch} etc. + +@d mu_to_glue_code eTeX_glue /*code for \.{\\mutoglue}*/ +@d glue_to_mu_code eTeX_mu /*code for \.{\\gluetomu}*/ + +@<Generate all \eTeX...@>= +primitive("mutoglue", last_item, mu_to_glue_code); +@!@:mu\_to\_glue\_}{\.{\\mutoglue} primitive@> +primitive("gluetomu", last_item, glue_to_mu_code); +@!@:glue\_to\_mu\_}{\.{\\gluetomu} primitive@> + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case mu_to_glue_code: print_esc("mutoglue");@+break; +case glue_to_mu_code: print_esc("gluetomu");@+break; + +@ @<Cases for fetching a glue value@>= +case mu_to_glue_code: scan_mu_glue(); + +@ @<Cases for fetching a mu value@>= +case glue_to_mu_code: scan_normal_glue(); + +@ \eTeX\ (in extended mode) supports 32768 (i.e., $2^{15}$) count, +dimen, skip, muskip, box, and token registers. As in \TeX\ the first +256 registers of each kind are realized as arrays in the table of +equivalents; the additional registers are realized as tree structures +built from variable-size nodes with individual registers existing only +when needed. Default values are used for nonexistent registers: zero +for count and dimen values, |zero_glue| for glue (skip and muskip) +values, void for boxes, and |null| for token lists (and current marks +discussed below). + +Similarly there are 32768 mark classes; the command \.{\\marks}|n| +creates a mark node for a given mark class |0 <= n <= 32767| (where +\.{\\marks0} is synonymous to \.{\\mark}). The page builder (actually +the |fire_up| routine) and the |vsplit| routine maintain the current +values of |top_mark|, |first_mark|, |bot_mark|, |split_first_mark|, and +|split_bot_mark| for each mark class. They are accessed as +\.{\\topmarks}|n| etc., and \.{\\topmarks0} is again synonymous to +\.{\\topmark}. As in \TeX\ the five current marks for mark class zero +are realized as |cur_mark| array. The additional current marks are +again realized as tree structure with individual mark classes existing +only when needed. + +@<Generate all \eTeX...@>= +primitive("marks", mark, marks_code); +@!@:marks\_}{\.{\\marks} primitive@> +primitive("topmarks", top_bot_mark, top_mark_code+marks_code); +@!@:top\_marks\_}{\.{\\topmarks} primitive@> +primitive("firstmarks", top_bot_mark, first_mark_code+marks_code); +@!@:first\_marks\_}{\.{\\firstmarks} primitive@> +primitive("botmarks", top_bot_mark, bot_mark_code+marks_code); +@!@:bot\_marks\_}{\.{\\botmarks} primitive@> +primitive("splitfirstmarks", top_bot_mark, split_first_mark_code+marks_code); +@!@:split\_first\_marks\_}{\.{\\splitfirstmarks} primitive@> +primitive("splitbotmarks", top_bot_mark, split_bot_mark_code+marks_code); +@!@:split\_bot\_marks\_}{\.{\\splitbotmarks} primitive@> + +@ The |scan_register_num| procedure scans a register number that must +not exceed 255 in compatibility mode resp.\ 32767 in extended mode. + +@<Declare \eTeX\ procedures for ex...@>= +static void scan_register_num(void); + +@ @<Declare procedures that scan restricted classes of integers@>= +static void scan_register_num(void) +{@+scan_int(); +if ((cur_val < 0)||(cur_val > max_reg_num)) + {@+print_err("Bad register code"); +@.Bad register code@> + help2(max_reg_help_line,"I changed this one to zero."); + int_error(cur_val);cur_val=0; + } +} + +@ @<Initialize variables for \eTeX\ comp...@>= +max_reg_num=255; +max_reg_help_line="A register number must be between 0 and 255."; + +@ @<Initialize variables for \eTeX\ ext...@>= +max_reg_num=32767; +max_reg_help_line="A register number must be between 0 and 32767."; + +@ @<Glob...@>= +static halfword @!max_reg_num; /*largest allowed register number*/ +static char *@!max_reg_help_line; /*first line of help message*/ + +@ There are seven almost identical doubly linked trees, one for the +sparse array of the up to 32512 additional registers of each kind and +one for the sparse array of the up to 32767 additional mark classes. +The root of each such tree, if it exists, is an index node containing 16 +pointers to subtrees for 4096 consecutive array elements. Similar index +nodes are the starting points for all nonempty subtrees for 4096, 256, +and 16 consecutive array elements. These four levels of index nodes are +followed by a fifth level with nodes for the individual array elements. + +Each index node is nine words long. The pointers to the 16 possible +subtrees or are kept in the |info| and |link| fields of the last eight +words. (It would be both elegant and efficient to declare them as +array, unfortunately \PASCAL\ doesn't allow this.) + +The fields in the first word of each index node and in the nodes for the +array elements are closely related. The |link| field points to the next +lower index node and the |sa_index| field contains four bits (one +hexadecimal digit) of the register number or mark class. For the lowest +index node the |link| field is |null| and the |sa_index| field indicates +the type of quantity (|int_val|, |dimen_val|, |glue_val|, |mu_val|, +|box_val|, |tok_val|, or |mark_val|). The |sa_used| field in the index +nodes counts how many of the 16 pointers are non-null. + +The |sa_index| field in the nodes for array elements contains the four +bits plus 16 times the type. Therefore such a node represents a count +or dimen register if and only if |sa_index < dimen_val_limit|; it +represents a skip or muskip register if and only if +|dimen_val_limit <= sa_index < mu_val_limit|; it represents a box register +if and only if |mu_val_limit <= sa_index < box_val_limit|; it represents a +token list register if and only if +|box_val_limit <= sa_index < tok_val_limit|; finally it represents a mark +class if and only if |tok_val_limit <= sa_index|. + +The |new_index| procedure creates an index node (returned in |cur_ptr|) +having given contents of the |sa_index| and |link| fields. + +@d box_val 4 /*the additional box registers*/ +@d mark_val 6 /*the additional mark classes*/ +@# +@d dimen_val_limit 0x20 /*$2^4\cdot(|dimen_val|+1)$*/ +@d mu_val_limit 0x40 /*$2^4\cdot(|mu_val|+1)$*/ +@d box_val_limit 0x50 /*$2^4\cdot(|box_val|+1)$*/ +@d tok_val_limit 0x60 /*$2^4\cdot(|tok_val|+1)$*/ +@# +@d index_node_size 9 /*size of an index node*/ +@d sa_index(A) type(A) /*a four-bit address or a type or both*/ +@d sa_used(A) subtype(A) /*count of non-null pointers*/ + +@<Declare \eTeX\ procedures for ex...@>= +static void new_index(quarterword @!i, pointer @!q) +{@+int k; /*loop index*/ +cur_ptr=get_node(index_node_size);sa_index(cur_ptr)=i; +sa_used(cur_ptr)=0;link(cur_ptr)=q; +for (k=1; k<=index_node_size-1; k++) /*clear all 16 pointers*/ + mem[cur_ptr+k]=sa_null; +} + +@ The roots of the seven trees for the additional registers and mark +classes are kept in the |sa_root| array. The first six locations must +be dumped and undumped; the last one is also known as |sa_mark|. + +@d sa_mark sa_root[mark_val] /*root for mark classes*/ + +@<Glob...@>= +static pointer @!sa_root0[mark_val-int_val+1], + *const @!sa_root = @!sa_root0-int_val; /*roots of sparse arrays*/ +static pointer @!cur_ptr; /*value returned by |new_index| and |find_sa_element|*/ +static memory_word @!sa_null; /*two |null| pointers*/ + +@ @<Set init...@>= +sa_mark=null;sa_null.hh.lh=null;sa_null.hh.rh=null; + +@ @<Initialize table...@>= +for (i=int_val; i<=tok_val; i++) sa_root[i]=null; + +@ Given a type |t| and a sixteen-bit number |n|, the |find_sa_element| +procedure returns (in |cur_ptr|) a pointer to the node for the +corresponding array element, or |null| when no such element exists. The +third parameter |w| is set |true| if the element must exist, e.g., +because it is about to be modified. The procedure has two main +branches: one follows the existing tree structure, the other (only used +when |w| is |true|) creates the missing nodes. + +We use macros to extract the four-bit pieces from a sixteen-bit register +number or mark class and to fetch or store one of the 16 pointers from +an index node. + +@d if_cur_ptr_is_null_then_return_or_goto(A) /*some tree element is missing*/ + {@+if (cur_ptr==null) + if (w) goto A;@+else return; + } +@# +@d hex_dig1(A) A/4096 /*the fourth lowest hexadecimal digit*/ +@d hex_dig2(A) (A/256)%16 /*the third lowest hexadecimal digit*/ +@d hex_dig3(A) (A/16)%16 /*the second lowest hexadecimal digit*/ +@d hex_dig4(A) A%16 /*the lowest hexadecimal digit*/ +@# +@d get_sa_ptr if (odd(i)) cur_ptr=link(q+(i/2)+1); + else cur_ptr=info(q+(i/2)+1) + /*set |cur_ptr| to the pointer indexed by |i| from index node |q|*/ +@d put_sa_ptr(A) if (odd(i)) link(q+(i/2)+1)=A; + else info(q+(i/2)+1)=A + /*store the pointer indexed by |i| in index node |q|*/ +@d add_sa_ptr {@+put_sa_ptr(cur_ptr);incr(sa_used(q)); + } /*add |cur_ptr| as the pointer indexed by |i| in index node |q|*/ +@d delete_sa_ptr {@+put_sa_ptr(null);decr(sa_used(q)); + } /*delete the pointer indexed by |i| in index node |q|*/ + +@<Declare \eTeX\ procedures for ex...@>= +static void find_sa_element(small_number @!t, halfword @!n, bool @!w) + /*sets |cur_val| to sparse array element location or |null|*/ +{@+ +pointer q; /*for list manipulations*/ +small_number @!i; /*a four bit index*/ +cur_ptr=sa_root[t]; +if_cur_ptr_is_null_then_return_or_goto(not_found);@/ +q=cur_ptr;i=hex_dig1(n);get_sa_ptr; +if_cur_ptr_is_null_then_return_or_goto(not_found1);@/ +q=cur_ptr;i=hex_dig2(n);get_sa_ptr; +if_cur_ptr_is_null_then_return_or_goto(not_found2);@/ +q=cur_ptr;i=hex_dig3(n);get_sa_ptr; +if_cur_ptr_is_null_then_return_or_goto(not_found3);@/ +q=cur_ptr;i=hex_dig4(n);get_sa_ptr; +if ((cur_ptr==null)&&w) goto not_found4; +return; +not_found: new_index(t, null); /*create first level index node*/ +sa_root[t]=cur_ptr;q=cur_ptr;i=hex_dig1(n); +not_found1: new_index(i, q); /*create second level index node*/ +add_sa_ptr;q=cur_ptr;i=hex_dig2(n); +not_found2: new_index(i, q); /*create third level index node*/ +add_sa_ptr;q=cur_ptr;i=hex_dig3(n); +not_found3: new_index(i, q); /*create fourth level index node*/ +add_sa_ptr;q=cur_ptr;i=hex_dig4(n); +not_found4: @<Create a new array element of type |t| with index |i|@>; +link(cur_ptr)=q;add_sa_ptr; +} + +@ The array elements for registers are subject to grouping and have an +|sa_lev| field (quite analogous to |eq_level|) instead of |sa_used|. +Since saved values as well as shorthand definitions (created by e.g., +\.{\\countdef}) refer to the location of the respective array element, +we need a reference count that is kept in the |sa_ref| field. An array +element can be deleted (together with all references to it) when its +|sa_ref| value is |null| and its value is the default value. +@^reference counts@> + +Skip, muskip, box, and token registers use two word nodes, their values +are stored in the |sa_ptr| field. +Count and dimen registers use three word nodes, their +values are stored in the |sa_int| resp.\ |sa_dim| field in the third +word; the |sa_ptr| field is used under the name |sa_num| to store +the register number. Mark classes use four word nodes. The last three +words contain the five types of current marks + +@d sa_lev sa_used /*grouping level for the current value*/ +@d pointer_node_size 2 /*size of an element with a pointer value*/ +@d sa_type(A) (sa_index(A)/16) /*type part of combined type/index*/ +@d sa_ref(A) info(A+1) /*reference count of a sparse array element*/ +@d sa_ptr(A) link(A+1) /*a pointer value*/ +@# +@d word_node_size 3 /*size of an element with a word value*/ +@d sa_num(A) sa_ptr(A) /*the register number*/ +@d sa_int(A) mem[A+2].i /*an integer*/ +@d sa_dim(A) mem[A+2].sc /*a dimension (a somewhat esotheric distinction)*/ +@# +@d mark_class_node_size 4 /*size of an element for a mark class*/ +@# +@d fetch_box(A) /*fetch |box(cur_val)|*/ + if (cur_val < 256) A=box(cur_val); + else{@+find_sa_element(box_val, cur_val, false); + if (cur_ptr==null) A=null;@+else A=sa_ptr(cur_ptr); + } + +@<Create a new array element...@>= +if (t==mark_val) /*a mark class*/ + {@+cur_ptr=get_node(mark_class_node_size); + mem[cur_ptr+1]=sa_null;mem[cur_ptr+2]=sa_null;mem[cur_ptr+3]=sa_null; + } +else{@+if (t <= dimen_val) /*a count or dimen register*/ + {@+cur_ptr=get_node(word_node_size);sa_int(cur_ptr)=0; + sa_num(cur_ptr)=n; + } + else{@+cur_ptr=get_node(pointer_node_size); + if (t <= mu_val) /*a skip or muskip register*/ + {@+sa_ptr(cur_ptr)=zero_glue;add_glue_ref(zero_glue); + } + else sa_ptr(cur_ptr)=null; /*a box or token list register*/ + } + sa_ref(cur_ptr)=null; /*all registers have a reference count*/ + } +sa_index(cur_ptr)=16*t+i;sa_lev(cur_ptr)=level_one + +@ The |delete_sa_ref| procedure is called when a pointer to an array +element representing a register is being removed; this means that the +reference count should be decreased by one. If the reduced reference +count is |null| and the register has been (globally) assigned its +default value the array element should disappear, possibly together with +some index nodes. This procedure will never be used for mark class +nodes. +@^reference counts@> + +@d add_sa_ref(A) incr(sa_ref(A)) /*increase reference count*/ +@# +@d change_box(A) /*change |box(cur_val)|, the |eq_level| stays the same*/ + if (cur_val < 256) box(cur_val)=A;@+else set_sa_box(A) +@# +@d set_sa_box(X) {@+find_sa_element(box_val, cur_val, false); + if (cur_ptr!=null) + {@+sa_ptr(cur_ptr)=X;add_sa_ref(cur_ptr);delete_sa_ref(cur_ptr); + } + } + +@<Declare \eTeX\ procedures for tr...@>= +static void delete_sa_ref(pointer @!q) /*reduce reference count*/ +{@+ +pointer p; /*for list manipulations*/ +small_number @!i; /*a four bit index*/ +small_number @!s; /*size of a node*/ +decr(sa_ref(q)); +if (sa_ref(q)!=null) return; +if (sa_index(q) < dimen_val_limit) + if (sa_int(q)==0) s=word_node_size; + else return; +else{@+if (sa_index(q) < mu_val_limit) + if (sa_ptr(q)==zero_glue) delete_glue_ref(zero_glue); + else return; + else if (sa_ptr(q)!=null) return; + s=pointer_node_size; + } +@/do@+{i=hex_dig4(sa_index(q));p=q;q=link(p);free_node(p, s); +if (q==null) /*the whole tree has been freed*/ + {@+sa_root[i]=null;return; + } +delete_sa_ptr;s=index_node_size; /*node |q| is an index node*/ +}@+ while (!(sa_used(q) > 0)); +} + +@ The |print_sa_num| procedure prints the register number corresponding +to an array element. + +@<Basic print...@>= +static void print_sa_num(pointer @!q) /*print register number*/ +{@+halfword @!n; /*the register number*/ +if (sa_index(q) < dimen_val_limit) n=sa_num(q); /*the easy case*/ +else{@+n=hex_dig4(sa_index(q));q=link(q);n=n+16*sa_index(q); + q=link(q);n=n+256*(sa_index(q)+16*sa_index(link(q))); + } +print_int(n); +} + +@ Here is a procedure that displays the contents of an array element +symbolically. It is used under similar circumstances as is +|restore_trace| (together with |show_eqtb|) for the quantities kept in +the |eqtb| array. + +@<Declare \eTeX\ procedures for tr...@>= +#ifdef @!STAT +static void show_sa(pointer @!p, char *@!s) +{@+small_number t; /*the type of element*/ +begin_diagnostic();print_char('{');print(s);print_char(' '); +if (p==null) print_char('?'); /*this can't happen*/ +else{@+t=sa_type(p); + if (t < box_val) print_cmd_chr(internal_register, p); + else if (t==box_val) + {@+print_esc("box");print_sa_num(p); + } + else if (t==tok_val) print_cmd_chr(toks_register, p); + else print_char('?'); /*this can't happen either*/ + print_char('='); + if (t==int_val) print_int(sa_int(p)); + else if (t==dimen_val) + {@+print_scaled(sa_dim(p));print("pt"); + } + else{@+p=sa_ptr(p); + if (t==glue_val) print_spec(p,"pt"); + else if (t==mu_val) print_spec(p,"mu"); + else if (t==box_val) + if (p==null) print("void"); + else{@+depth_threshold=0;breadth_max=1;show_node_list(p); + } + else if (t==tok_val) + {@+if (p!=null) show_token_list(link(p), null, 32); + } + else print_char('?'); /*this can't happen either*/ + } + } +print_char('}');end_diagnostic(false); +} +#endif + +@ Here we compute the pointer to the current mark of type |t| and mark +class |cur_val|. + +@<Compute the mark pointer...@>= +{@+find_sa_element(mark_val, cur_val, false); +if (cur_ptr!=null) + if (odd(t)) cur_ptr=link(cur_ptr+(t/2)+1); + else cur_ptr=info(cur_ptr+(t/2)+1); +} + +@ The current marks for all mark classes are maintained by the |vsplit| +and |fire_up| routines and are finally destroyed (for \.{INITEX} only) +@.INITEX@> +by the |final_cleanup| routine. Apart from updating the current marks +when mark nodes are encountered, these routines perform certain actions +on all existing mark classes. The recursive |do_marks| procedure walks +through the whole tree or a subtree of existing mark class nodes and +preforms certain actions indicted by its first parameter |a|, the action +code. The second parameter |l| indicates the level of recursion (at +most four); the third parameter points to a nonempty tree or subtree. +The result is |true| if the complete tree or subtree has been deleted. + +@d vsplit_init 0 /*action code for |vsplit| initialization*/ +@d fire_up_init 1 /*action code for |fire_up| initialization*/ +@d fire_up_done 2 /*action code for |fire_up| completion*/ +@d destroy_marks 3 /*action code for |final_cleanup|*/ +@# +@d sa_top_mark(A) info(A+1) /*\.{\\topmarks}|n|*/ +@d sa_first_mark(A) link(A+1) /*\.{\\firstmarks}|n|*/ +@d sa_bot_mark(A) info(A+2) /*\.{\\botmarks}|n|*/ +@d sa_split_first_mark(A) link(A+2) /*\.{\\splitfirstmarks}|n|*/ +@d sa_split_bot_mark(A) info(A+3) /*\.{\\splitbotmarks}|n|*/ + +@<Declare the function called |do_marks|@>= +static bool do_marks(small_number @!a, small_number @!l, pointer @!q) +{@+int i; /*a four bit index*/ +if (l < 4) /*|q| is an index node*/ + {@+for (i=0; i<=15; i++) + {@+get_sa_ptr; + if (cur_ptr!=null) if (do_marks(a, l+1, cur_ptr)) delete_sa_ptr; + } + if (sa_used(q)==0) + {@+free_node(q, index_node_size);q=null; + } + } +else /*|q| is the node for a mark class*/ + {@+switch (a) { + @<Cases for |do_marks|@>@; + } /*there are no other cases*/ + if (sa_bot_mark(q)==null) if (sa_split_bot_mark(q)==null) + {@+free_node(q, mark_class_node_size);q=null; + } + } +return(q==null); +} + +@ At the start of the |vsplit| routine the existing |split_fist_mark| +and |split_bot_mark| are discarded. + +@<Cases for |do_marks|@>= +case vsplit_init: if (sa_split_first_mark(q)!=null) + {@+delete_token_ref(sa_split_first_mark(q));sa_split_first_mark(q)=null; + delete_token_ref(sa_split_bot_mark(q));sa_split_bot_mark(q)=null; + } @+break; + +@ We use again the fact that |split_first_mark==null| if and only if +|split_bot_mark==null|. + +@<Update the current marks for |vsplit|@>= +{@+find_sa_element(mark_val, mark_class(p), true); +if (sa_split_first_mark(cur_ptr)==null) + {@+sa_split_first_mark(cur_ptr)=mark_ptr(p); + add_token_ref(mark_ptr(p)); + } +else delete_token_ref(sa_split_bot_mark(cur_ptr)); +sa_split_bot_mark(cur_ptr)=mark_ptr(p); +add_token_ref(mark_ptr(p)); +} + +@ At the start of the |fire_up| routine the old |top_mark| and +|first_mark| are discarded, whereas the old |bot_mark| becomes the new +|top_mark|. An empty new |top_mark| token list is, however, discarded +as well in order that mark class nodes can eventually be released. We +use again the fact that |bot_mark!=null| implies |first_mark!=null|; it +also knows that |bot_mark==null| implies |top_mark==first_mark==null|. + +@<Cases for |do_marks|@>= +case fire_up_init: if (sa_bot_mark(q)!=null) + {@+if (sa_top_mark(q)!=null) delete_token_ref(sa_top_mark(q)); + delete_token_ref(sa_first_mark(q));sa_first_mark(q)=null; + if (link(sa_bot_mark(q))==null) /*an empty token list*/ + {@+delete_token_ref(sa_bot_mark(q));sa_bot_mark(q)=null; + } + else add_token_ref(sa_bot_mark(q)); + sa_top_mark(q)=sa_bot_mark(q); + } @+break; + +@ @<Cases for |do_marks|@>= +case fire_up_done: if ((sa_top_mark(q)!=null)&&(sa_first_mark(q)==null)) + {@+sa_first_mark(q)=sa_top_mark(q);add_token_ref(sa_top_mark(q)); + } @+break; + +@ @<Update the current marks for |fire_up|@>= +{@+find_sa_element(mark_val, mark_class(p), true); +if (sa_first_mark(cur_ptr)==null) + {@+sa_first_mark(cur_ptr)=mark_ptr(p); + add_token_ref(mark_ptr(p)); + } +if (sa_bot_mark(cur_ptr)!=null) delete_token_ref(sa_bot_mark(cur_ptr)); +sa_bot_mark(cur_ptr)=mark_ptr(p);add_token_ref(mark_ptr(p)); +} + +@ Here we use the fact that the five current mark pointers in a mark +class node occupy the same locations as the the first five pointers of +an index node. For systems using a run-time switch to distinguish +between \.{VIRTEX} and \.{INITEX}, the codewords `$|@t\#\&{ifdef} \.{INIT}@>|\ldots|@t\#\&{endif}@>|$' +surrounding the following piece of code should be removed. +@.INITEX@> +@^system dependencies@> + +@<Cases for |do_marks|@>= +#ifdef @!INIT +case destroy_marks: for (i=top_mark_code; i<=split_bot_mark_code; i++) + {@+get_sa_ptr; + if (cur_ptr!=null) + {@+delete_token_ref(cur_ptr);put_sa_ptr(null); + } + } +#endif + +@ The command code |internal_register| is used for `\.{\\count}', `\.{\\dimen}', +etc., as well as for references to sparse array elements defined by +`\.{\\countdef}', etc. + +@<Cases of |register| for |print_cmd_chr|@>= +{@+if ((chr_code < mem_bot)||(chr_code > lo_mem_stat_max)) + cmd=sa_type(chr_code); +else{@+cmd=chr_code-mem_bot;chr_code=null; + } +if (cmd==int_val) print_esc("count"); +else if (cmd==dimen_val) print_esc("dimen"); +else if (cmd==glue_val) print_esc("skip"); +else print_esc("muskip"); +if (chr_code!=null) print_sa_num(chr_code); +} + +@ Similarly the command code |toks_register| is used for `\.{\\toks}' as +well as for references to sparse array elements defined by +`\.{\\toksdef}'. + +@<Cases of |toks_register| for |print_cmd_chr|@>= +{@+print_esc("toks"); +if (chr_code!=mem_bot) print_sa_num(chr_code); +} + +@ When a shorthand definition for an element of one of the sparse arrays +is destroyed, we must reduce the reference count. + +@<Cases for |eq_destroy|@>= +case toks_register: case internal_register: + if ((equiv_field(w) < mem_bot)||(equiv_field(w) > lo_mem_stat_max)) + delete_sa_ref(equiv_field(w));@+break; + +@ The task to maintain (change, save, and restore) register values is +essentially the same when the register is realized as sparse array +element or entry in |eqtb|. The global variable |sa_chain| is the head +of a linked list of entries saved at the topmost level |sa_level|; the +lists for lowel levels are kept in special save stack entries. + +@<Glob...@>= +static pointer @!sa_chain; /*chain of saved sparse array entries*/ +static quarterword @!sa_level; /*group level for |sa_chain|*/ + +@ @<Set init...@>= +sa_chain=null;sa_level=level_zero; + +@ The individual saved items are kept in pointer or word nodes similar +to those used for the array elements: a word node with value zero is, +however, saved as pointer node with the otherwise impossible |sa_index| +value |tok_val_limit|. + +@d sa_loc(A) sa_ref(A) /*location of saved item*/ + +@<Declare \eTeX\ procedures for tr...@>= +static void sa_save(pointer @!p) /*saves value of |p|*/ +{@+pointer q; /*the new save node*/ +quarterword @!i; /*index field of node*/ +if (cur_level!=sa_level) + {@+check_full_save_stack;save_type(save_ptr)=restore_sa; + save_level(save_ptr)=sa_level;save_index(save_ptr)=sa_chain; + incr(save_ptr);sa_chain=null;sa_level=cur_level; + } +i=sa_index(p); +if (i < dimen_val_limit) + {@+if (sa_int(p)==0) + {@+q=get_node(pointer_node_size);i=tok_val_limit; + } + else{@+q=get_node(word_node_size);sa_int(q)=sa_int(p); + } + sa_ptr(q)=null; + } +else{@+q=get_node(pointer_node_size);sa_ptr(q)=sa_ptr(p); + } +sa_loc(q)=p;sa_index(q)=i;sa_lev(q)=sa_lev(p); +link(q)=sa_chain;sa_chain=q;add_sa_ref(p); +} + +@ @<Declare \eTeX\ procedures for tr...@>= +static void sa_destroy(pointer @!p) /*destroy value of |p|*/ +{@+if (sa_index(p) < mu_val_limit) delete_glue_ref(sa_ptr(p)); +else if (sa_ptr(p)!=null) + if (sa_index(p) < box_val_limit) flush_node_list(sa_ptr(p)); + else delete_token_ref(sa_ptr(p)); +} + +@ The procedure |sa_def| assigns a new value to sparse array elements, +and saves the former value if appropriate. This procedure is used only +for skip, muskip, box, and token list registers. The counterpart of +|sa_def| for count and dimen registers is called |sa_w_def|. + +@d sa_define(A, B, C, D, E) if (e) + if (global) gsa_def(A, B);@+else sa_def(A, B); + else if (global) geq_define(C, D, E);@+else eq_define(C, D, E) +@# +@d sa_def_box /*assign |cur_box| to |box(cur_val)|*/ + {@+find_sa_element(box_val, cur_val, true); + if (global) gsa_def(cur_ptr, cur_box);@+else sa_def(cur_ptr, cur_box); + } +@# +@d sa_word_define(A, B) if (e) + if (global) gsa_w_def(A, B);@+else sa_w_def(A, B); + else word_define(A, B) + +@<Declare \eTeX\ procedures for tr...@>= +static void sa_def(pointer @!p, halfword @!e) + /*new data for sparse array elements*/ +{@+add_sa_ref(p); +if (sa_ptr(p)==e) + { +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"reassigning"); +#endif +@;@/ + sa_destroy(p); + } +else{ +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"changing"); +#endif +@;@/ + if (sa_lev(p)==cur_level) sa_destroy(p);@+else sa_save(p); + sa_lev(p)=cur_level;sa_ptr(p)=e; +#ifdef @!STAT + if (tracing_assigns > 0) show_sa(p,"into"); +#endif +@;@/ + } +delete_sa_ref(p); +} +@# +static void sa_w_def(pointer @!p, int @!w) +{@+add_sa_ref(p); +if (sa_int(p)==w) + { +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"reassigning"); +#endif +@;@/ + } +else{ +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"changing"); +#endif +@;@/ + if (sa_lev(p)!=cur_level) sa_save(p); + sa_lev(p)=cur_level;sa_int(p)=w; +#ifdef @!STAT + if (tracing_assigns > 0) show_sa(p,"into"); +#endif +@;@/ + } +delete_sa_ref(p); +} + +@ The |sa_def| and |sa_w_def| routines take care of local definitions. +@^global definitions@> +Global definitions are done in almost the same way, but there is no need +to save old values, and the new value is associated with |level_one|. + +@<Declare \eTeX\ procedures for tr...@>= +static void gsa_def(pointer @!p, halfword @!e) /*global |sa_def|*/ +{@+add_sa_ref(p); +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"globally changing"); +#endif +@;@/ +sa_destroy(p);sa_lev(p)=level_one;sa_ptr(p)=e; +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"into"); +#endif +@;@/ +delete_sa_ref(p); +} +@# +static void gsa_w_def(pointer @!p, int @!w) /*global |sa_w_def|*/ +{@+add_sa_ref(p); +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"globally changing"); +#endif +@;@/ +sa_lev(p)=level_one;sa_int(p)=w; +#ifdef @!STAT +if (tracing_assigns > 0) show_sa(p,"into"); +#endif +@;@/ +delete_sa_ref(p); +} + +@ The |sa_restore| procedure restores the sparse array entries pointed +at by |sa_chain| + +@<Declare \eTeX\ procedures for tr...@>= +static void sa_restore(void) +{@+pointer p; /*sparse array element*/ +@/do@+{p=sa_loc(sa_chain); +if (sa_lev(p)==level_one) + {@+if (sa_index(p) >= dimen_val_limit) sa_destroy(sa_chain); +#ifdef @!STAT + if (tracing_restores > 0) show_sa(p,"retaining"); +#endif +@;@/ + } +else{@+if (sa_index(p) < dimen_val_limit) + if (sa_index(sa_chain) < dimen_val_limit) sa_int(p)=sa_int(sa_chain); + else sa_int(p)=0; + else{@+sa_destroy(p);sa_ptr(p)=sa_ptr(sa_chain); + } + sa_lev(p)=sa_lev(sa_chain); +#ifdef @!STAT + if (tracing_restores > 0) show_sa(p,"restoring"); +#endif +@;@/ + } +delete_sa_ref(p); +p=sa_chain;sa_chain=link(p); +if (sa_index(p) < dimen_val_limit) free_node(p, word_node_size); +else free_node(p, pointer_node_size); +}@+ while (!(sa_chain==null)); +} + +@ When reading \.{\\patterns} while \.{\\savinghyphcodes} is positive +the current |lc_code| values are stored together with the hyphenation +patterns for the current language. They will later be used instead of +the |lc_code| values for hyphenation purposes. + +The |lc_code| values are stored in the linked trie analogous to patterns +$p_1$ of length~1, with |hyph_root==trie_r[0]| replacing |trie_root| and +|lc_code(p_1)| replacing the |trie_op| code. This allows to compress +and pack them together with the patterns with minimal changes to the +existing code. + +@d hyph_root trie_r[0] /*root of the linked trie for |hyph_codes|*/ + +@<Initialize table entries...@>= +hyph_root=0;hyph_start=0; + +@ @<Store hyphenation codes for current language@>= +{@+c=cur_lang;first_child=false;p=0; +@/do@+{q=p;p=trie_r[q]; +}@+ while (!((p==0)||(c <= so(trie_c[p])))); +if ((p==0)||(c < so(trie_c[p]))) + @<Insert a new trie node between |q| and |p|, and make |p| point to it@>; +q=p; /*now node |q| represents |cur_lang|*/ +@<Store all current |lc_code| values@>; +} + +@ We store all nonzero |lc_code| values, overwriting any previously +stored values (and possibly wasting a few trie nodes that were used +previously and are not needed now). We always store at least one +|lc_code| value such that |hyph_index| (defined below) will not be zero. + +@<Store all current |lc_code| values@>= +p=trie_l[q];first_child=true; +for (c=0; c<=255; c++) + if ((lc_code(c) > 0)||((c==255)&&first_child)) + {@+if (p==0) + @<Insert a new trie node between |q| and |p|, and make |p| point to +it@>@; + else trie_c[p]=si(c); + trie_o[p]=qi(lc_code(c)); + q=p;p=trie_r[q];first_child=false; + } +if (first_child) trie_l[q]=0;@+else trie_r[q]=0 + +@ We must avoid to ``take'' location~1, in order to distinguish between +|lc_code| values and patterns. + +@<Pack all stored |hyph_codes|@>= +{@+if (trie_root==0) for (p=0; p<=255; p++) trie_min[p]=p+2; +first_fit(hyph_root);trie_pack(hyph_root); +hyph_start=trie_ref[hyph_root]; +} + +@ The global variable |hyph_index| will point to the hyphenation codes +for the current language. + +@d set_hyph_index /*set |hyph_index| for current language*/ + if (trie_char(hyph_start+cur_lang)!=qi(cur_lang) + ) hyph_index=0; /*no hyphenation codes for |cur_lang|*/ + else hyph_index=trie_link(hyph_start+cur_lang) +@# +@d set_lc_code(A) /*set |hc[0]| to hyphenation or lc code for |A|*/ + if (hyph_index==0) hc[0]=lc_code(A); + else if (trie_char(hyph_index+A)!=qi(A)) hc[0]=0; + else hc[0]=qo(trie_op(hyph_index+A)) + +@<Glob...@>= +static trie_pointer @!hyph_start; /*root of the packed trie for |hyph_codes|*/ +static trie_pointer @!hyph_index; /*pointer to hyphenation codes for |cur_lang|*/ + +@ When |saving_vdiscards| is positive then the glue, kern, and penalty +nodes removed by the page builder or by \.{\\vsplit} from the top of a +vertical list are saved in special lists instead of being discarded. + +@d tail_page_disc disc_ptr[copy_code] /*last item removed by page builder*/ +@d page_disc disc_ptr[last_box_code] /*first item removed by page builder*/ +@d split_disc disc_ptr[vsplit_code] /*first item removed by \.{\\vsplit}*/ + +@<Glob...@>= +static pointer @!disc_ptr0[vsplit_code-copy_code+1], + *const @!disc_ptr = @!disc_ptr0-copy_code; /*list pointers*/ + +@ @<Set init...@>= +page_disc=null;split_disc=null; + +@ The \.{\\pagediscards} and \.{\\splitdiscards} commands share the +command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are +distinguished by their |chr_code| values |last_box_code| and +|vsplit_code|. These |chr_code| values are larger than |box_code| and +|copy_code|. + +@<Generate all \eTeX...@>= +primitive("pagediscards", un_vbox, last_box_code);@/ +@!@:page\_discards\_}{\.{\\pagediscards} primitive@> +primitive("splitdiscards", un_vbox, vsplit_code);@/ +@!@:split\_discards\_}{\.{\\splitdiscards} primitive@> + +@ @<Cases of |un_vbox| for |print_cmd_chr|@>=; +else if (chr_code==last_box_code) print_esc("pagediscards"); +else if (chr_code==vsplit_code) print_esc("splitdiscards") + +@ @<Handle saved items and |goto done|@>= +{@+link(tail)=disc_ptr[cur_chr];disc_ptr[cur_chr]=null; +goto done; +} + +@ The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, +and \.{\\displaywidowpenalties} commands allow to define arrays of +penalty values to be used instead of the corresponding single values. + +@d inter_line_penalties_ptr equiv(inter_line_penalties_loc) +@<Generate all \eTeX...@>= +primitive("interlinepenalties", set_shape, inter_line_penalties_loc);@/ +@!@:inter\_line\_penalties\_}{\.{\\interlinepenalties} primitive@> +primitive("clubpenalties", set_shape, club_penalties_loc);@/ +@!@:club\_penalties\_}{\.{\\clubpenalties} primitive@> +primitive("widowpenalties", set_shape, widow_penalties_loc);@/ +@!@:widow\_penalties\_}{\.{\\widowpenalties} primitive@> +primitive("displaywidowpenalties", set_shape, display_widow_penalties_loc);@/ +@!@:display\_widow\_penalties\_}{\.{\\displaywidowpenalties} primitive@> + +@ @<Cases of |set_shape| for |print_cmd_chr|@>= +case inter_line_penalties_loc: print_esc("interlinepenalties");@+break; +case club_penalties_loc: print_esc("clubpenalties");@+break; +case widow_penalties_loc: print_esc("widowpenalties");@+break; +case display_widow_penalties_loc: print_esc("displaywidowpenalties"); + +@ @<Fetch a penalties array element@>= +{@+scan_int(); +if ((equiv(m)==null)||(cur_val < 0)) cur_val=0; +else{@+if (cur_val > penalty(equiv(m))) cur_val=penalty(equiv(m)); + cur_val=penalty(equiv(m)+cur_val); + } +} + +@ |expand_depth| and |expand_depth_count| are used in the \eTeX\ code +above, but not defined. So we correct this in the following modules, +|expand_depth| having been defined by us as an integer paramater (hence +there is a new primitive to create in \eTeX\ mode), and +|expand_depth_count| needing to be a global. Both have to be defined to +some sensible value. + +@<Glob...@>= +static int @!expand_depth_count; /*current expansion depth*/ + +@ @<Generate all \eTeX...@>= +primitive("expanddepth", assign_int, int_base+expand_depth_code);@/ +@!@:expand\_depth\_}{\.{\\expanddepth} primitive@> + +@ @<Cases for |print_param|@>= +case expand_depth_code: print_esc("expanddepth");@+break; + +@ @<Initialize variables for \eTeX\ extended mode@>= +expand_depth=10000; /*value taken for compatibility with Web2C*/ +expand_depth_count=0; + +@* The extended features of \Prote. +\Prote\ extends furthermore \eTeX\ i.e. \eTeX\ is thus required +before adding \Prote\ own extensions. But if \eTeX\ mode has not +be enabled, the engine is still compatible with \TeX with no added +primitive commands and with a modification of code---from +\eTeX\ exclusively for now---that is sufficiently minor so that +the engine still deserves the name \TeX. + +@d Prote_ex (Prote_mode==1) /*is this prote mode?*/ + +@<Glob...@>= +static int @!Prote_mode; /*to be or not to be; but an int to dump*/ + +@ We begin in \TeX\ compatibility mode. The state |Prote_mode| will be +set to $1$ only if activated by the supplementary `*' added to the one +activating the \eTeX\ extensions (in fact, this means for the user two +initial `*' in a row). + +@<Initialize table entries...@>= +Prote_mode=0; /*initially we are in compatibility mode*/ + +@ @<Dump the \Prote\ state@>= +dump_int(Prote_mode); + +@ @<Undump the \Prote\ state@>= +undump(0, 1, Prote_mode); + +@ In order to not clobber the global scope with variables that are +locally used, the initializations for \Prote, if the mode is +activated, are done in a dedicated procedure. These are not part of +what is dumped. + +@<Last-minute procedures@>= +static void Prote_initialize(void) +{@+int k; /*all-purpose index*/ +@<\Prote\ initializations@>; +} + +@ There are commands and command modifiers, these command modifiers +maybe encoding too a type. So we must not step on each other toes. + +@ When we are adding primitives that deal intimately with the variables +of \TeX, in the |eqtb| regions (in our case regions 5 for integers, and +6 for dimensions), the command modifier to the various +\.{assign\_*} classes is simply the address. So we have interpolated +our added variables above since this is done by the way of WEB +pre-processing. + +@ For the conditional primitives, the way is straightforward. + +@d if_incsname_code (eTeX_last_if_test_cmd_mod+1) /* `\.{\\ifincsname}' */ +@d if_primitive_code (eTeX_last_if_test_cmd_mod+2) /* `\.{\\ifprimitive}' */ +@ The |last_item| class is for secondary internal values, that can be +dereferenced by \.{\\the} but are read-only and are mainly related to +the value of a current state or are such values but their assignation +shall trigger an action, and we shall not hook in the \.{assign\_*} +processing. + +The command modifiers for the |last_item| class were, originally, +encoding too the type of the item (see m.410). But \eTeX\ has added its +extensions and we won't try to be smart: the type |cur_val_level| will +be set by switching between contiguous ranges of values of the same +type. + +And we will define here all the instances of |last_item| that we add +in order to keep our number assignations gathered. + +@d Prote_version_code (eTeX_last_last_item_cmd_mod+1) + /*code for \.{\\Proteversion}*/ +@d random_seed_code (eTeX_last_last_item_cmd_mod+2) /* \.{\\randomseed} */ +@d elapsed_time_code (eTeX_last_last_item_cmd_mod+3) /* \.{\\elapsedtime} */ +@d shell_escape_code (eTeX_last_last_item_cmd_mod+4) /* \.{\\shellescape} */ +@d last_xpos_code (eTeX_last_last_item_cmd_mod+5) /* \.{\\lastxpos} */ +@d last_ypos_code (eTeX_last_last_item_cmd_mod+6) /* \.{\\lastypos} */ + +@ The \.{convert} class is for conversion of some external stuff to put +it, as a token list, into the scanner. It is not an internal value that +could be dereferenced by \.{\\the} and it is obviously not settable: it +expands to the token list. + +@d Prote_revision_code (eTeX_last_convert_cmd_mod+1) /* \.{\\Proterevision}*/ +@d strcmp_code (eTeX_last_convert_cmd_mod+2) /* \.{\\strcmp} */ +@d set_random_seed_code (eTeX_last_convert_cmd_mod+3) /* \.{\\setrandomseed} */ +@d normal_deviate_code (eTeX_last_convert_cmd_mod+4) /* \.{\\normaldeviate} */ +@d uniform_deviate_code (eTeX_last_convert_cmd_mod+5) /* \.{\\uniformdeviate} */ +@d creation_date_code (eTeX_last_convert_cmd_mod+6) /* \.{\\creationdate} */ +@d file_size_code (eTeX_last_convert_cmd_mod+7) /* \.{\\filesize} */ +@d file_mod_date_code (eTeX_last_convert_cmd_mod+8) /* \.{\\filemodedate} */ +@d file_dump_code (eTeX_last_convert_cmd_mod+9) /* \.{\\filedump} */ +@d mdfive_sum_code (eTeX_last_convert_cmd_mod+10) /* \.{\\mdfivesum} */ +@d Prote_last_convert_code mdfive_sum_code + +@ When modifying the meaning of something---in this case, for now, +switching to the primitive meaning if it exists---or modifying the +way expansion is done, it seems that it can be thought as a special +case of expansion, hence a variant of |expand_after|. + +@d primitive_code (eTeX_last_expand_after_cmd_mod+1) /* `\.{\\primitive}' */ +@d expanded_code (eTeX_last_expand_after_cmd_mod+2) /* `\.{\\expanded}' */ +@ When the primitive manipulate something really external, whether +trying to insert something in the output format---\.{DVI} for +us---or dealing with the system, it doesn't fit in any cmd group and +could be called an exception. So it will be a variant of the |extension| +cmd group. + +\eTeX\ didn't add new primitives to the extension command group, so we +add a related macro, equal to |TeX_last_extension_cmd_mod|, simply so +that it is locally obvious. + +@d eTeX_last_extension_cmd_mod TeX_last_extension_cmd_mod /*none added*/ +@d reset_timer_code (eTeX_last_extension_cmd_mod+1) /* `\.{\\resettimer}' */ +@d save_pos_code (eTeX_last_extension_cmd_mod+2) /* `\.{\\savepos}' */ + +@*1 Identifying \Prote. + +We will start by giving a mean to test that \Prote\ is activated and +to identify the version. + +@<Generate all \Prote\ primitives@>= +if (!pdf_on) { +primitive("Proteversion", last_item, Prote_version_code); +@!@:Prote\_version\_}{\.{\\Proteversion} primitive@> +primitive("Proterevision", convert, Prote_revision_code);@/ +@!@:Prote\_revision\_}{\.{\\Proterevision} primitive@> +} +@ We use the different hooks added to insert our cases. + +@<Cases of |last_item| for |print_cmd_chr|@>= +case Prote_version_code: print_esc("Proteversion");@+break; + +@ @<Cases for fetching a \Prote\ int value@>= +case Prote_version_code: cur_val=Prote_version;@+break; + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case Prote_revision_code: print_esc("Proterevision");@+break; + +@ @<Cases of `Scan the argument for command |c|'@>= +case Prote_revision_code: do_nothing;@+break; + +@ @<Cases of `Print the result of command |c|'@>= +case Prote_revision_code: print(Prote_revision);@+break; + +@*1 \Prote\ added token lists routines. + +We will, more than once, convert a general normally expanded text +to a string. Due to the unfelicity of \PASCAL\ about forward +declarations of functions, we declare procedures that do their task +by defining global variables. In this case, |garbage| is used. + +|link(garbage)| will hold the pointer to the head of the token list, +|info(garbage)| to the tail. If the two are equals, then the list is +empty. The routine making a string will take |link(garbage)| and +put the number in |info(garbage)|. + +@ The first procedure scan a general text (normally) expanded. The +head of the reference count is returned in |link(garbage)|, the +tail in |info(garbage)| and if the two are equals, the list is empty. +User must keep in mind that this has to be flushed when done with! + +@<Forward declarations@>= +static void scan_general_x_text(void); + +@ @<Declare \Prote\ procedures for token lists@>= +static void scan_general_x_text(void) +{@+pointer d; /*to save |def_ref|*/ +d=def_ref;info(garbage)=scan_toks(false, true); +link(garbage)=def_ref; +def_ref=d; /*restore whatever*/ +} + +@ The second procedure takes a token list defined in |link(garbage)| +and converts it to a string number that is returned in |info(garbage)|. +Neither the token list nor the string (obviously) are flushed. + +@<Forward declarations@>= +static void toks_to_str(void); + +@ Here we are using |token_show| that has to take a reference count. + +@<Declare \Prote\ procedures for token lists@>= +static void toks_to_str(void) +{@+int old_setting; /*holds |selector| setting*/ +old_setting=selector;selector=new_string; + token_show(link(garbage));selector=old_setting; + str_room(1); /*flirting with the limit means probably truncation*/ + info(garbage)=make_string(); +} + +@*1 \Prote\ added strings routines. + +The next procedure sets |name_of_file| from the string given as an +argument, mimicking the |input| primitive by adding an |.tex| extension +if there is none. It silently truncates if the length of the string +exceeds the size of the name buffer and doesn't use |cur_area| and +|cur_ext|, but |name_length| is set to the real name length (without +truncating) so a test about |k <= file_name_size| allows to detect the +impossibility of opening the file without having to call external code. +The string is not flushed: it is the responsability of the code calling +the procedure to flush it if wanted. + +@<Declare \Prote\ procedures for strings@>= +static void str_to_name(str_number @!s) +{@+int k; /*number of positions filled in |name_of_file|*/ +ASCII_code @!c; /*character being packed*/ +int @!j; /*index into |str_pool|*/ +k=0;for (j=str_start[s]; j<=str_start[s+1]-1; j++) {@+ + c=so(str_pool[j]);incr(k); + if (k <= file_name_size) name_of_file[k]=xchr[c]; + } +name_length=k; +name_of_file[name_length+1]=0; +} + +@*1 Exchanging data with external routines. + +In order to try to sever external handling from our core, we +introduce an all purpose exchange buffer |xchg_buffer|, that will +be an array of bytes (these can be interpreted as \.{text\_char} +or \.{ASCII\_char} or \.{eight\_bits}). + +The data to be used starts at index $1$ and ends at index +|xchg_buffer_length|. + +For the moment, this buffer must accommodate a numerical MD5 +hash value, i.e. $16$ bytes long; will also be used to exchange $64$ +bytes chunks to feed MD5 hash generation, and will have to accommodate +too the maximal size of the date returned by \.{\\creationdate} or +\.{\\filemoddate} that is $23$ \.{text\_char}. So at least $64$ for now. + +@<Global...@>= +static eight_bits @!xchg_buffer0[xchg_buffer_size], + *const @!xchg_buffer = @!xchg_buffer0-1; + /*exchange buffer for interaction with system routines*/ +static int @!xchg_buffer_length; /*last valid index in this buf; 0 means no data*/ +@ @<Check \Prote\ ``constant'' values for consistency@>= +if (xchg_buffer_size < 64) bad=51; + +@ When there is data in the exchange buffer, the length of the data has +to be set. When an external routine has consumed the data, it shall +reset the length to $0$. + +@<\Prote\ initializations@>= +xchg_buffer_length=0; + +@*1 \Prote\ states. + +\.{\\shellescape} depends on a pdf\TeX\ feature, namely the ability to +escape to shell. There is no such thing in \Prote. So it expands to $0$. +Note: this a status primitive; it does not allow to set the status but +simply expands to a read-only integer reflecting it. In \Prote, it is +always $0$. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) { +primitive("pdfshellescape", last_item, shell_escape_code); +@!@:pdfshellescape\_}{\.{\\pdfshellescape} primitive@> +} else { primitive("shellescape", last_item, shell_escape_code); +@!@:shellescape\_}{\.{\\shellescape} primitive@> +} + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case shell_escape_code: + if (pdf_on) print_esc("pdfshellescape"); + else print_esc("shellescape"); + @+break; + +@ @<Cases for fetching a \Prote\ int value@>= +case shell_escape_code: cur_val=0;@+break; + +@*1 \Prote\ conditionals. + +We add the following conditionals, that are susceptible of the same +expansion rules as the other |if_test| ones. + +@<Generate all \Prote\ primitives@>= +primitive("ifincsname", if_test, if_incsname_code); +@!@:if\_incsname\_}{\.{\\if} primitive@> +if (pdf_on) +primitive("ifpdfprimitive", if_test, if_primitive_code); +@!@:if\_pdfprimitive\_}{\.{\\if} pdfprimitive@> +else primitive("ifprimitive", if_test, if_primitive_code); +@!@:if\_primitive\_}{\.{\\if} primitive@> + + +@ @<Cases of |if_test| for |print_cmd_chr|@>= +case if_incsname_code: print_esc("ifincsname");@+break; +case if_primitive_code: + if (pdf_on) print_esc("ifpdfprimitive"); + else print_esc("ifprimitive"); + @+break; + +@ The conditional \.{\\ifincsname} is simple since we increment a global +variable |incsname_state| when we enter the \.{\\csname} command and +decrement it when we have reached and passed the \.{\\endcsname}---a +scope depth index. + +@<\Prote\ initializations@>= +incsname_state=0; + +@ @<Cases for |conditional|@>= +case if_incsname_code: b=(incsname_state > 0);@+break; + +@ The conditional \.{\\ifprimitive} is true when the following control +sequence is a primitive; false otherwise. |id_lookup| can return +|undefined_control_sequence| (for a control sequence not entered in the +hash since |no_new_control_sequence| is |true|), but since it has the +\.{eq\_type} set to |undefined_cs|, the test of this latter works as +for a control sequence entered but never defined. + +@ @<Cases for |conditional|@>= +case if_primitive_code: {@+@/do@+{get_token();}@+ while (!(cur_tok!=space_token)); +if ((cur_cs!=0)&&(cur_cmd!=undefined_cs)&&(cur_cmd < call)) b=true;else b=false; +} @+break; + +@*1 \Prote\ primitives changing definition or expansion. + +The next primitives, here, are more involved since they are whether +changing the definition of a control sequence, or modifying how the +tokens will be treated. + +@ Since a user level control sequence can give a new definition to a +primitive, the \.{primitive}... primitive, if the argument is a control +sequence whose name is the name of a primitive, will make this primitive +meaning the meaning of the control sequence {\it hic et nunc}. If there +was no primitive meaning, no error is raised and nothing is changed. +It can be seen as a kind of \.{expand\_after} command since it is in the +external handling of the token list creation. + +Since we need to redefine the token and hence give a valid control +sequence in the |eqtb|, we have defined |frozen_primitive|. This +``frozen'' is, actually, not quite frozen by itself since we will +redefine its values according to the primitive definition we have +to restablish momentarily. But it is indeed ``permanent'' since +it only refers to the permanently defined meanings. Hence, the +initialization of the |frozen_primitive| address is just to document +the code: these values will be overwritten on each actual call. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfprimitive", expand_after, primitive_code); +@!@:pdfprimitive\_}{\.{\\pdfprimitive} primitive@> +else +primitive("primitive", expand_after, primitive_code); +@!@:primitive\_}{\.{\\primitive} primitive@> +text(frozen_primitive)=text(cur_val);eqtb[frozen_primitive]=eqtb[cur_val]; + +@ @<Cases of |expandafter| for |print_cmd_chr|@>= +case primitive_code: + if (pdf_on) print_esc("pdfprimitive"); + else print_esc("primitive"); + @+break; + +@ The problem is that the primitives are added at |level_one| and that a +redefinition as a macro at this same level by a user simply overwrites +the definition. We need then to keep these definitions. + +Primitives are only added by \.{INITEX}. So we can consider what we will +call a |ROM|, since it can be only ``flashed'' by \.{INITEX} and is +read-only afterwards, a kind of BIOS table holding initial system calls +(primitives). + +Since primitives are not macros (they don't need to expand or to +evaluate parameters since their definition is directly in the code), +the definition of a primitive is a couple: the command class (|cur_cmd|) +and the modifier (|cur_chr|) to distinguish between the cases---the +instances. But since, at the user level, a primitive is identified by +its name, and that a redefinition is, mandatorily, a homonym, the +location of the macro shadowing the primitive is at the same address as +was the primitive in the |eqtb|. So in order to speed-up the check, we +should organize things so that the address in the |eqtb| of a control +sequence (one character or multiletter) can be readily +converted in an address in the |ROM| array. + +This array will be an array of memory word, of type |two_halves|, in +order to re-use the macro definitions set for the table of equivalents. + +The one character primitives are added by direct addressing relative to +|single_base|. The multiletter primitives are added starting at +|frozen_control_sequence-1|, downwards; but there are only, at the +moment, $322$ multileter primitives defined by \TeX, $78$ such +primitives defined by \eTeX, and we are adding $24$ more. It is clear +that, looking at primitives, region 2 of |eqtb| is really a sparse array +and that, when |hash_size| is increased for format needs, there will be +a fair amount of space wasted if we simply copy, in fact, second part +of region 1 and region 2 in the |ROM|. + +Yes, but it is simpler as a first approach---premature optimization is +the root of all evil. So a simple translation scheme will be enough. + +The index in |ROM| will start at $1$ and will go up to $256 + 1 + +hash\_size$, that is a simple translation from |single_base| to +|ROM_base|, but only for addresses of interest, the other pointing to +an |ROM_undefined_primitive| that will allow an easy test. + +@d ROM_base 1 +@d ROM_size (256+1+hash_size) /*256 oc, undefined and ml*/ +@d ROM_undefined_primitive 257 +@d ROM_type_field(A) A.hh.b0 +@d ROM_equiv_field(X) X.hh.rh +@d ROM_type(A) ROM_type_field(ROM[A]) /*command code for equivalent*/ +@d set_ROM_p_from_cs(A) if ((A >= single_base) + &&(A < frozen_control_sequence)) + p=A-single_base+ROM_base; + else p=ROM_undefined_primitive + +@<Global...@>= +static memory_word @!ROM0[ROM_size-ROM_base+1], + *const @!ROM = @!ROM0-ROM_base; + +@ Even if it will be unused in \TeX\ or \eTeX\ modes, we will initialize +it since we add code to the |primitive| procedure and we need \TeX\ +and \eTeX\ ones to be registered as well, whether \.{INITEX} switches to +\Prote\ mode later or not. + +@<Initialize table entries...@>= +ROM[ROM_undefined_primitive]=eqtb[undefined_control_sequence]; +for (k=ROM_base; k<=256; k++) ROM[k]=ROM[ROM_undefined_primitive]; +for (k=ROM_undefined_primitive+1; k<=ROM_size; k++) + ROM[k]=ROM[ROM_undefined_primitive]; + +@ When a primitive is added---and this only happens in \.{INITEX}---we +have to define the corresponding address in the \.{ROM}. + +@ |cur_val| has the pointer in second part of region 1 or in region 2 +of |eqtb|. + +@<Add primitive definition to the |ROM| array@>= +set_ROM_p_from_cs(cur_val); +ROM[p]=eqtb[cur_val]; + +@ This array has to be dumped since it is only defined by \.{INITEX}. It +is always dumped even if it is unused unless in \Prote\ mode. + +@<Dump the |ROM| array@>= +for (k=ROM_base; k<=ROM_size; k++) dump_wd(ROM[k]); + +@ And what has been dumped shall be undumped. + +@<Undump the |ROM| array@>= +for (k=ROM_base; k<=ROM_size; k++) undump_wd(ROM[k]); + +@ Once all this is done, the processing of \.{\\primitive} is simple: +we read the next token that has to be a control sequence. If this +control sequence belongs to region 1 or 2 and is defined in |ROM|, we +redefine the token to be the |frozen_primitive| control sequence, +redefining its codes from the |ROM| and setting the text associated for +printing purposes. If not, the token is unchanged. Then we put back the +token so that it will be processed again, maybe redefined. + +@<Cases for |expandafter|@>= +case primitive_code: {@+get_token(); +set_ROM_p_from_cs(cur_cs); +if ((p!=ROM_undefined_primitive)&&(ROM_type(p)!=undefined_cs)) {@+ + eqtb[frozen_primitive]=ROM[p]; + text(frozen_primitive)=text(cur_cs); + cur_tok=cs_token_flag+frozen_primitive; + } +back_input(); +} @+break; + +@ The next primitive changes the expansion of its argument that is like +a general text expanded, except that protected macros (an \eTeX\ +extension) are not extanded. + +@ @<Generate all \Prote\ primitives@>= +primitive("expanded", expand_after, expanded_code);@/ +@!@:expanded\_}{\.{\\expanded} primitive@> + +@ @<Cases of |expandafter| for |print_cmd_chr|@>= +case expanded_code: print_esc("expanded"); + +@ This intervenes in |expand| and we must substitute a token list to our +current token, putting it back for further reprocessing. + +@<Cases for |expandafter|@>= +case expanded_code: {@+scan_general_x_text(); + back_list(link(link(garbage))); + free_avail(link(garbage)); /*drop reference count*/ + } + +@*1 \Prote\ strings related primitives. + +The primitive \.{\\strcmp} text two parameters that are general text +without expansion. The two token lists created are converted to strings +and this couple of strings is then compared, character by character. If +the first string is lexicographically sorted before the second, the +expansion is $-1$; if the two strings are equal, the expansion is $0$; +if the first string is lexicographically sorted after the second, the +expansion is $1$. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfstrcmp", convert, strcmp_code);@/ +@!@:pdf\_strcmp\_}{\.{\\pdfstrcmp} primitive@> +else +primitive("strcmp", convert, strcmp_code); +@!@:strcmp\_}{\.{\\strcmp} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case strcmp_code: + if (pdf_on) print_esc("pdfstrcmp"); + else print_esc("strcmp"); + @+break; + +@ It should be noted that the strings comparison is \TeX\ strings +comparison: the arguments are subject to the manipulation done when +scanning a general text (squeezing non escaped blanks), and the +characters are converted according to the |xord| array. Thus it is +an |ASCII_code|---in the \TeX\ sense explained at the very beginning +of the web file, part 2---comparison and the result is the same, +as long as relative characters are mapped to the same value, whatever +the system. Nul strings are valid. + +@<Cases of `Scan the argument for command |c|'@>= +case strcmp_code: @<Scan the argument for |strcmp_code|@>@;@+break; + +@ @<Scan the argument for |strcmp_code|@>= +{@+scan_general_x_text();toks_to_str(); + s=info(garbage);flush_list(link(garbage)); + scan_general_x_text();toks_to_str(); + t=info(garbage);flush_list(link(garbage)); + if ((length(s)==0)&&(length(t)==0)) cur_val=0; + else if (length(s)==0) cur_val=-1; + else if (length(t)==0) cur_val=1; + else{@+m=str_start[s];n=str_start[t];r=false; + while ((!r)&&(m < str_start[s+1])&&(n < str_start[t+1])) {@+ + cur_val=str_pool[m]-str_pool[n];if (cur_val!=0) r=true; + incr(m);incr(n); + } + if (cur_val==0) {@+if (length(s)!=length(t)) + if (m!=str_start[s+1]) cur_val=1;else cur_val=-1; + } + else cur_val=cur_val/(double)abs(cur_val); + } + flush_string;flush_string; + } + +@ @<Cases of `Print the result of command |c|'@>= +case strcmp_code: print_int(cur_val);@+break; + +@*1 \Prote\ date and time related primitives. + +The following primitives are related to the time elapsed since a +defined moment in time. The creation date is fixed at the moment when +|fix_date_and_time| has been called and stays fixed afterwards. This +moment is also, by default, the reference moment for computing the time +elapsed. + +@ The creation date is retrieved by the \.{\\creationdate} primitive. As +explained above, the date corresponds to the moment when +|fix_date_and_time| was called taking into account |FORCE_SOURCE_DATE| +and |SOURCE_DATE_EPOCH| (see above, m.241). If the creation date is +forced, the string will be UTC related. + +The format of the string is |D: YYYYMMDDHHmmSSOHH"mm"|, `O' being the +relationship of local time to UT, that is `-' (minus), `+' or `Z'; +HH followed by a single quote being the absolute value of the offset +from UT in hours (00--23), mm followed by a single quote being the +absolute value of the offset from UT in minutes (00--59). All fields +after the year are optional and default to zero values. + +@ @<Generate all \Prote\ primitives@>= +if (!pdf_on) +primitive("pdfcreationdate", convert, creation_date_code);@/ +@!@:pdf\_creation\_date\_}{\.{\\pdfcreationdate} primitive@> +else +primitive("creationdate", convert, creation_date_code);@/ +@!@:creationdate\_}{\.{\\creationdate} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case creation_date_code: + if (pdf_on) print_esc("pdfcreationdate"); + else print_esc("creationdate"); + @+break; + +@ |get_creation_date| has to be provided by the system. +@^system dependencies@> + +@<Cases of `Scan the argument for command |c|'@>= +case creation_date_code: get_creation_date();@+break; + +@ The date is in the |time_str| so we have simply to convert the +characters. + +@<Cases of `Print the result of command |c|'@>= +case creation_date_code: for (k=0; time_str[k]!='\0'; k++) + print_char(time_str[k]);@+break; + +@ The time elapsed is a scaled integer the unit being scaled seconds, +i.e. $1/65536$ of a second. Since our scaled integers have a +defined range, the value can not reach or pass, in plain seconds, +$32767$. + +The elapsed time returned is relative to some defined moment. At +start, the reference moment is the time the date was set for +|fix_date_and_time|. This requires system support and the default +implementation here will then fix this moment at noon on 4 July 1776 +and what would be returned by the function is here simply defined by +a macro: with this reference time and this basic code, |infinity| is +the permanent answer. +@^system dependencies@> + +@d get_elapsed_time infinity /*a function should be implemented*/ + +@ @<Generate all \Prote\ primitives@>= +if (pdf_on) { +primitive("pdfresettimer", extension, reset_timer_code);@/ +@!@:reset\_timer\_}{\.{\\pdfresettimer} primitive@> +primitive("pdfelapsedtime", last_item, elapsed_time_code); +@!@:elapsed\_time\_}{\.{\\pdfelapsedtime} primitive@> +} else { +primitive("resettimer", extension, reset_timer_code);@/ +@!@:resettimer\_}{\.{\\resettimer} primitive@> +primitive("elapsedtime", last_item, elapsed_time_code);@/ +@!@:elapsedtime\_}{\.{\\elapsedtime} primitive@> +} + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case elapsed_time_code: + if (pdf_on) print_esc("pdfelapsedtime"); + else print_esc("elapsedtime"); + @+break; + +@ @<Cases of |extension| for |print_cmd_chr|@>= +case reset_timer_code: + if (pdf_on) print_esc("pdfresettimer"); + else print_esc("resettimer"); + @+break; + +@ @<Cases for fetching a \Prote\ int value@>= +case elapsed_time_code: + { double t=(start_sec*1.0+start_nsec/1000000000.0-reset_timer)*0x10000; + if (t>=infinity) cur_val=infinity; + else cur_val=(scaled)t; + @+break; + } + +@ The reference moment can be reset by a call to the primitive +\.{\\resettimer}. It simply resets the reference moment to the moment +the primitive was called. The counter is not regularily incremented. +When asked about the time elapsed what is returned is the difference, in +scaled seconds, from the moment of the call to the moment of reference. +So there is no persistent variable neither a kind of clock implemented. +@^system dependencies@> + +Since we maintain the time anyway, we implement the timing function. +@<Glob...@>= +static double reset_timer=0.0; /*time of the last reset in seconds*/ + + +@ Since to reset the timer a simple call to the routine is necessary, +we simply add it to \.{main\_control} by adding it to the cases handled +by \.{do\_extension}. It contributes nothing to the token list: it is a +``fire and forget'', so no need to handle the special \.{subtype} in the +other hooks. + +@<Cases for |do_extension|@>= +case reset_timer_code: reset_timer=start_sec*1.0+start_nsec/1000000000.0;@+break; + +@*1 \Prote\ file related primitives. + +The presence of the following primitives in the engine can be +questioned. Since they are very external, and their implementation, for +example in C, requires things that are not in the C standard (the +date of modification of the file, for example). So these should not be +multiplied. + +@ The \.{\\filesize} primitive expands to the size, in bytes, of the +file. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdffilesize", convert, file_size_code);@/ +@!@:pdf\_file\_size\_}{\.{\\pdffilesize} primitive@> +else +primitive("filesize", convert, file_size_code); +@!@:filesize\_}{\.{\\filesize} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case file_size_code: + if (pdf_on) print_esc("pdffilesize"); + else print_esc("filesize"); + @+break; + +@ In order to be able to treat the problem when trying to open the file, +we open here and pass the file pointer, if success, to a dedicated +function in order to get its size. In case of problem, nothing is +returned. +@^system dependencies@> + +@<Cases of `Scan the argument for command |c|'@>= +case file_size_code: @<Scan the argument for |file_size_code|@>@;@+break; + +@ @<Scan the argument for |file_size_code|@>= +{@+scan_general_x_text();toks_to_str(); + s=info(garbage);flush_list(link(garbage));str_to_name(s); + cur_val=-1; /*invalid value if error*/ + cur_val=get_file_size(); + flush_string; +} + +@ @<Cases of `Print the result of command |c|'@>= +case file_size_code: if (cur_val!=-1) print_int(cur_val);@+break; + +@ The \.{\\filemoddate} expands to a date with the same format as the +creation date (see \.{\\creationdate}). + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdffilemoddate", convert, file_mod_date_code);@/ +@!@:pdf\_file\_mod\_date\_}{\.{\\pdffilemoddate} primitive@> +else +primitive("filemoddate", convert, file_mod_date_code); +@!@:filemoddate\_}{\.{\\filemoddate} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case file_mod_date_code: + if (pdf_on) print_esc("pdffilemoddate"); + else print_esc("filemoddate"); + @+break; + +@ For getting the argument, the treatment resembles that of +\.{\\filesize} obviously, since it is only the type of information +returned that changes. The availability of this information in system +dependent. The information shall be set in |xchg_buffer|. +@^system dependencies@> + +In this basic implementation, we set the string to the empty one by +simply setting |xchg_buffer_length| to $0$. + +@d get_file_mtime xchg_buffer_length=0 + +@<Cases of `Scan the argument for command |c|'@>= +case file_mod_date_code: @<Scan the argument for |file_mod_date_code|@>@;@+break; + +@ @<Scan the argument for |file_mod_date_code|@>= +{@+scan_general_x_text();toks_to_str(); + s=info(garbage);flush_list(link(garbage));str_to_name(s); + get_file_mod_date(); + flush_string; +} @+break; + +@ Printing the result consists simply in printing every +|text_char| in |time_str|. +If the length is $0$, nothing is printed. + +@<Cases of `Print the result of command |c|'@>= +case file_mod_date_code: for (k=0; time_str[k]!='\0'; k++) + print_char(time_str[k]);@+break; + +@ The primitive \.{\\filedump} expands to the dump of the first + \.{length} bytes of the file, starting from \.{offset}. Offset and +length are optional integers given, in that order, introduced resp. +by the keywords ``offset'' and ``length''. If not specified, they +default to $0$. A length of $0$ expands to nothing (it is not an error). +The file name is given as a |general text|. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdffiledump", convert, file_dump_code);@/ +@!@:pdf\_file\_dump\_}{\.{\\pdffiledump} primitive@> +else +primitive("filedump", convert, file_dump_code); +@!@:filedump\_}{\.{\\filedump} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case file_dump_code: + if (pdf_on) print_esc("pdffiledump"); + else print_esc("filedump"); + @+break; + +@ The scanning of the arguments is obvious from the syntax above. + +Since ``offset'' and ``length'' may be given in that order, we assign +the variables \.{k} and \.{l}, in alphabetical order. These have to be +positive or nul values. + +Contrary to other blocks, and for optimization purposes (in order not to +clobber the string pool with data that we can read, when necessary, one +byte at a time), \.{k}, \.{l} and \.{f} will be defined here and used +when printing. + +@<Cases of `Scan the argument for command |c|'@>= +case file_dump_code: @<Scan the argument for |file_dump_code|@>@;@+break; + +@ @<Scan the argument for |file_dump_code|@>= +{@+k=0;l=0; /*defaults*/ + if (scan_keyword("offset")) {@+scan_int(); + if (cur_val < 0) {@+print_err("Bad ");print_esc("filedump"); +@.Bad \\filedump@> + help2("I allow only nonnegative values here.",@/ + "I changed this one to zero.");int_error(cur_val); + } + else k=cur_val; + } + if (scan_keyword("length")) {@+scan_int(); + if (cur_val < 0) {@+print_err("Bad ");print_esc("filedump"); +@.Bad \\filedump@> + help2("I allow only nonnegative values here.",@/ + "I changed this one to zero.");int_error(cur_val); + } + else l=cur_val; + } + scan_general_x_text();toks_to_str();s=info(garbage); + flush_list(link(garbage));str_to_name(s); + flush_string; /*this one was the filename argument*/ + } + +@ The variables have been set, and the file name has been defined. We +simply print the uppercase hexadecimal transcription of every byte +requested before closing the file. Here we deal with bytes +(\.{eight\_bits} values) so there is no transcription. + +@<Cases of `Print the result of command |c|'@>= +case file_dump_code: + { FILE *f=fopen((char*)name_of_file0,"rb"); + if (f!=NULL) {@+ + fseek(f,k,SEEK_SET); + do@+{i=fgetc(f); if (i==EOF) break; + dig[0]=i%16;dig[1]=i/16; + print_the_digs(2);decr(l); + }@+ while (!(feof(f)||(l==0))); + fclose(f); + } + } @+break; + +@ The \.{\\mdfivesum} is obviously a variant of the \.{convert} class since +it takes values from external and put them as a token list in the +stream. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfmdfivesum", convert, mdfive_sum_code);@/ +@!@:pdf\_mdfive\_sum\_}{\.{\\pdfmdfivesum} primitive@> +else +primitive("mdfivesum", convert, mdfive_sum_code); +@!@:mdfivesum\_}{\.{\\mdfivesum} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case mdfive_sum_code: + if (pdf_on) print_esc("pdfmdfivesum"); + else print_esc("mdfivesum"); + @+break; + +@ There is an optional keyword "file" that will tell us if the | < general +text > | is to be taken as a filename or just as the string to hash. The +| < balanced text > | is expanded in both cases. + +Once this is done, we ask to init the MD5 state; then fill the exchange +buffer with chunks of data and update the MD5 hash with every chunk +until source is exhausted and ask for the final ($16$ bytes numerical +value) result that will be put in the |xchg_buffer|. + +Since we are looking for a ``general text'', that must be enclosed (at +least: ended; the opening brace can be implicit) by a |right_brace|, +an error will be caught with runaways. + +The general text is converted to a string. It is legal to have an empty +string if the argument is not a file. + +@<Cases of `Scan the argument for command |c|'@>= +case mdfive_sum_code: @<Scan the argument for |mdfive_sum_code|@>@; @+break; + +@ @<Scan the argument for |mdfive_sum_code|@>= +{@+r=scan_keyword("file");scan_general_x_text(); +toks_to_str();s=info(garbage);flush_list(link(garbage)); +l=get_md5_sum(s,r); +flush_string; /*done with the filename or string to hash*/ +} + +@ As a result, there is $16$ bytes in the |md5_digest| representing the +MD5 hash. We simply print, byte by byte, the uppercase hexadecimal +representation of this hash. + +@<Cases of `Print the result of command |c|'@>= +case mdfive_sum_code: for (k=0; k<l; k++) +{@+dig[0]=md5_digest[k]%16;@+dig[1]=md5_digest[k]/16;print_the_digs(2); +} @+break; + +@*1 Pseudo-random number generation. + +These routines come from John Hobby's \MP\ and generate 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 |mpfract_one-1|, inclusive. + +\MP\ uses 28~significant bits of precision and we have kept this in +order for the routines to behave the same way as in \MP. So the name +|mpfract| will be used instead of |scaled|, while the two are integers, +in the range defined by \TeX. + +@d double(A) A=A+A /*multiply a variable by two*/ +@d halfp(A) (A)/2 /*when quantity is known to be positive or zero*/ + +@ 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...@>= +static int @!two_to_the[31]; /*powers of two*/ +static int @!spec_log0[28], *const @!spec_log = @!spec_log0-1; /*special logarithms*/ + +@ @<\Prote\ initializations@>= +two_to_the[0]=1; +for (k=1; k<=30; k++) 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; k<=27; k++) 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.) + +@<Declare \Prote\ arithmetic routines@>= +static scaled m_log(scaled @!x) +{@+int @!y, @!z; /*auxiliary registers*/ +int @!k; /*iteration counter*/ +if (x <= 0) @<Handle non-positive logarithm@>@; +else{@+y=1302456956+4-100; /*$14\times2^{27}\ln2\approx1302456956.421063$*/ + z=27595+6553600; /*and $2^{16}\times .421063\approx 27595$*/ + while (x < mpfract_four) + {@+double(x);y=y-93032639;z=z-48782; + } /*$2^{27}\ln2\approx 93032639.74436163$ + and $2^{16}\times.74436163\approx 48782$*/ + y=y+(z/unity);k=2; + while (x > mpfract_four+4) + @<Increase |k| until |x| can be multiplied by a factor of $2^{-k}$, and +adjust $y$ accordingly@>; + return y/8; + } +} + +@ @<Increase |k| until |x| can...@>= +{@+z=((x-1)/two_to_the[k])+1; /*$z=\lceil x/2^k\rceil$*/ +while (x < mpfract_four+z) + {@+z=halfp(z+1);k=k+1; + } +y=y+spec_log[k];x=x-z; +} + +@ @<Handle non-positive logarithm@>= +{@+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();return 0; +} + +@ Here is introduced the special 28bits significand |mpfract|. + +@d el_gordo 017777777777 /*$2^{31}-1$, the largest value that \TeX\ likes*/ +@d mpfract_half 01000000000 /*$2^{27}$, represents 0.50000000*/ +@d mpfract_one 02000000000 /*$2^{28}$, represents 1.00000000*/ +@d mpfract_four 010000000000 /*$2^{30}$, represents 4.00000000*/ + +@<Types...@>= +typedef int mpfract; /*this type is used for pseudo-random numbers*/ + +@ The |make_mpfract| routine produces the |mpfract| equivalent of +|p/(double)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_mpfract(t, t)==mpfract| is valid; +and it's also possible to use the subroutine ``backwards,'' using +the relation |make_mpfract(t, mpfract)==t| between scaled types. + +If the result would have magnitude $2^{31}$ or more, |make_mpfract| +sets |arith_error=true|. Most of \TeX'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)/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. + +The present implementation is highly portable, but slow; it avoids +multiplication and division except in the initial stage. But since it is +not part of \TeX\ inner loop, it doesn't matter. + +@<Declare \Prote\ arithmetic routines@>= +static mpfract make_mpfract(int @!p, int @!q) +{@+int @!f; /*the fraction bits, with a leading 1 bit*/ +int @!n; /*the integer part of $\vert p/q\vert$*/ +bool @!negative; /*should the result be negated?*/ +int @!be_careful; /*disables certain compiler optimizations*/ +if (p >= 0) negative=false; +else{@+negate(p);negative=true; + } +if (q <= 0) + { +#ifdef @!DEBUG +if (q==0) confusion("/");@; +#endif +@;@/ +@:this can't happen /}{\quad \./@> + negate(q);negative=!negative; + } +n=p/q;p=p%q; +if (n >= 8) + {@+arith_error=true; + if (negative) return-el_gordo;@+else return el_gordo; + } +else{@+n=(n-1)*mpfract_one; + @<Compute $f=\lfloor 2^{28}(1+p/q)+{1\over2}\rfloor$@>; + if (negative) return-(f+n);@+else return f+n; + } +} + +@ The |@/do@+{| 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; +@/do@+{be_careful=p-q;p=be_careful+p; +if (p >= 0) f=f+f+1; +else{@+double(f);p=p+q; + } +}@+ while (!(f >= mpfract_one)); +be_careful=p-q; +if (be_careful+p >= 0) incr(f) + +@ The dual of |make_mpfract| is |take_mpfract|, 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|. + +@<Declare \Prote\ arithmetic routines@>= +static int take_mpfract(int @!q, mpfract @!f) +{@+int @!p; /*the fraction so far*/ +bool @!negative; /*should the result be negated?*/ +int @!n; /*additional multiple of $q$*/ +int @!be_careful; /*disables certain compiler optimizations*/ +@<Reduce to the case that |f>=0| and |q>0|@>; +if (f < mpfract_one) n=0; +else{@+n=f/mpfract_one;f=f%mpfract_one; + if (q <= el_gordo/n) n=n*q; + else{@+arith_error=true;n=el_gordo; + } + } +f=f+mpfract_one; +@<Compute $p=\lfloor qf/2^{28}+{1\over2}\rfloor-q$@>; +be_careful=n-el_gordo; +if (be_careful+p > 0) + {@+arith_error=true;n=el_gordo-p; + } +if (negative) return-(n+p); +else return n+p; +} + +@ @<Reduce to the case that |f>=0| and |q>0|@>= +if (f >= 0) negative=false; +else{@+negate(f);negative=true; + } +if (q < 0) + {@+negate(q);negative=!negative; + } + +@ 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=mpfract_half; /*that's $2^{27}$; the invariants hold now with $k=28$*/ +if (q < mpfract_four) + @/do@+{if (odd(f)) p=halfp(p+q);@+else p=halfp(p); + f=halfp(f); + }@+ while (!(f==1)); +else@/do@+{if (odd(f)) p=p+halfp(q-p);@+else p=halfp(p); + f=halfp(f); + }@+ while (!(f==1)) + +@ 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. + +@<Glob...@>= +static mpfract @!randoms[55]; /*the last 55 random values generated*/ +static int @!j_random; /*the number of unused |randoms|*/ + +@ This array of pseudo-random numbers is set starting from a seed value, +that is kept in the global integer |random_seed|. + +@<Global...@>= +static int @!random_seed; /*seed for pseudo-random number generation*/ + +@ @<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfrandomseed", last_item, random_seed_code); +@!@:random\_seed\_}{\.{\\pdfrandomseed} primitive@> +else +primitive("randomseed", last_item, random_seed_code);@/ +@!@:randomseed\_}{\.{\\randomseed} primitive@> + + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case random_seed_code: + if (pdf_on) print_esc("pdfrandomseed"); + else print_esc("randomseed"); + @+break; + +@ @<Cases for fetching a \Prote\ int value@>= +case random_seed_code: cur_val=random_seed;@+break; + +@ We set the initial value from the system time. System integrators +could provide a better source of pseudo-randomness. + +Every time a new seed value is assigned, the array has to be +regenerated for consumption by routines explained a little later. + +@<\Prote\ initializations@>= +random_seed=sys_time; +init_randoms(); + +@ Since changing the value must trigger the redefinition of the array, +a dedicated primitive is defined to take the new seed and call +|init_randoms|. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfsetrandomseed", convert, set_random_seed_code);@/ +@!@:set\_random\_seed\_code}{\.{\\pdfsetrandomseed} primitive@> +else +primitive("setrandomseed", convert, set_random_seed_code);@/ +@!@:setrandomseed\_}{\.{\\setrandomseed} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case set_random_seed_code: + if (pdf_on) print_esc("pdfsetrandomseed"); + else print_esc("setrandomseed"); + @+break; + +@ Once we have retrieved and redefined |random_seed|, we must regenerate +the |randoms| array. + +@<Cases of `Scan the argument for command |c|'@>= +case set_random_seed_code: {@+scan_int();random_seed=cur_val; + init_randoms(); + } @+break; + +@ @<Cases of `Print the result of command |c|'@>= +case set_random_seed_code: print_int(random_seed);@+break; + +@ 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) new_randoms(); + else decr(j_random) + +@<Declare \Prote\ arithmetic routines@>= +static void new_randoms(void) +{@+int @!k; /*index into |randoms|*/ +int @!x; /*accumulator*/ +for (k=0; k<=23; k++) + {@+x=randoms[k]-randoms[k+31]; + if (x < 0) x=x+mpfract_one; + randoms[k]=x; + } +for (k=24; k<=54; k++) + {@+x=randoms[k]-randoms[k-24]; + if (x < 0) x=x+mpfract_one; + randoms[k]=x; + } +j_random=54; +} + +@ To initialize the |randoms| table, we call the following routine. + +@<Declare \Prote\ arithmetic routines@>= +static void init_randoms(void) +{@+mpfract @!j, @!jj, @!k; /*more or less random integers*/ +int @!i; /*index into |randoms|*/ +j=abs(random_seed); +while (j >= mpfract_one) j=halfp(j); +k=1; +for (i=0; i<=54; i++) + {@+jj=k;k=j-k;j=jj; + if (k < 0) k=k+mpfract_one; + randoms[(i*21)%55]=j; + } +new_randoms();new_randoms();new_randoms(); /*``warm up'' the array*/ +} + +@ 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 |mult_integers| 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. + +@<Declare \Prote\ arithmetic routines@>= +static scaled unif_rand(scaled @!x) +{@+scaled @!y; /*trial value*/ +next_random;y=take_mpfract(abs(x), randoms[j_random]); +if (y==abs(x)) return 0; +else if (x > 0) return y; +else return-y; +} + +@ This can be used by calling the following primitive. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfuniformdeviate", convert, uniform_deviate_code);@/ +@!@:uniform\_deviate\_}{\.{\\pdfuniformdeviate} primitive@> +else +primitive("uniformdeviate", convert, uniform_deviate_code);@/ +@!@:uniformdeviate\_}{\.{\\uniformdeviate} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case uniform_deviate_code: + if (pdf_on) print_esc("pdfuniformdeviate"); + else print_esc("uniformdeviate"); + @+break; + +@ It takes one integer argument obviously that will be the argument +to the function. + +@<Cases of `Scan the argument for command |c|'@>= +case uniform_deviate_code: {@+scan_int(); + cur_val=unif_rand(cur_val); + } @+break; + +@ @<Cases of `Print the result of command |c|'@>= +case uniform_deviate_code: print_int(cur_val);@+break; + +@ 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(A) {@+return A; + } + +@<Declare \Prote\ arithmetic routines@>= +static int ab_vs_cd(int @!a, int b, int c, int d) +{@+ +int @!q, @!r; /*temporary registers*/ +@<Reduce to the case that |a,c>=0|, |b,d>0|@>; +loop@+{@+q=a/d;r=c/b; + if (q!=r) + if (q > r) return_sign(1)@;@+else return_sign(-1); + q=a%d;r=c%b; + if (r==0) + if (q==0) return_sign(0)@;@+else return_sign(1); + if (q==0) return_sign(-1); + a=b;b=q;c=d;d=r; + } /*now |a > d > 0| and |c > b > 0|*/ +} + +@ @<Reduce to the case that |a...@>= +if (a < 0) + {@+negate(a);negate(b); + } +if (c < 0) + {@+negate(c);negate(d); + } +if (d <= 0) + {@+if (b >= 0) + if (((a==0)||(b==0))&&((c==0)||(d==0))) return_sign(0)@; + else return_sign(1); + if (d==0) + if (a==0) return_sign(0)@;@+else return_sign(-1); + q=a;a=c;c=q;q=-b;b=-d;d=q; + } +else if (b <= 0) + {@+if (b < 0) if (a > 0) return_sign(-1); + if (c==0) return_sign(0)@;else return_sign(-1); + } + +@ 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\/}). + +@<Declare \Prote\ arithmetic routines@>= +static scaled norm_rand(void) +{@+int @!x, @!u, @!l; /*what the book would call $2^{16}X$, $2^{28}U$, + and $-2^{24}\ln U$*/ +@/do@+{ + @/do@+{next_random; + x=take_mpfract(112429, randoms[j_random]-mpfract_half); + /*$2^{16}\sqrt{8/e}\approx 112428.82793$*/ + next_random;u=randoms[j_random]; + }@+ while (!(abs(x) < u)); +x=make_mpfract(x, u); +l=139548960-m_log(u); /*$2^{24}\cdot12\ln2\approx139548959.6165$*/ +}@+ while (!(ab_vs_cd(1024, l, x, x) >= 0)); +return x; +} + +@ This can be used by calling the following primitive. + +@<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfnormaldeviate", convert, normal_deviate_code);@/ +@!@:normal\_deviate\_}{\.{\\pdfnormaldeviate} primitive@> +else +primitive("normaldeviate", convert, normal_deviate_code);@/ +@!@:normaldeviate\_}{\.{\\normaldeviate} primitive@> + + +@ @<Cases of |convert| for |print_cmd_chr|@>= +case normal_deviate_code: + if (pdf_on) print_esc("pdfnormaldeviate"); + else print_esc("normaldeviate"); + @+break; + + +@ @<Cases of `Scan the argument for command |c|'@>= +case normal_deviate_code: cur_val=norm_rand();@+break; + +@ @<Cases of `Print the result of command |c|'@>= +case normal_deviate_code: print_int(cur_val);@+break; + +@*1 DVI related primitives. + +These primitives are related to positions in the DVI output. + +The \TeX\ and DVI system coordinates relate to an origin that is at the +upper left corner. The \TeX\ coordinates are computed relative to an +origin that has $(0,0)$ coordinates. Coordinates grow then rightward and +downward. This is the {\sl page} coordinates relative to what is +typeset (what \TeX\ is dealing with). + +But this typesetting material has to be put on what we will call {\sl +paper}. The material put into shape by \TeX\ is put on the paper. On +this paper, where will be put the \TeX\ origin? It is considered to be +$1in$ at the right and $1in$ down from the upper left corner of the +paper (see m.590, alinea 2). + +@d DVI_std_x_offset 4736286 /*1 inch in sp*/ +@d DVI_std_y_offset 4736286 /*1 inch in sp*/ + +@ But the paper size is not specified in the DVI file and is not being +dealt with by \TeX. + +In order to have a common reference point, and since the \.{\\lastxpos} +and \.{\\lastypos} primitives originated in pdf\TeX, these two +primitives give positions, in scaled points, relative to the lower left +corner of the paper. + +Hence the need, for these primitive, to define the paper size, with the +(misnamed) \.{\\pagewidth} and \.{\\pageheight}. + +\.{\\pagewidth} and \.{\\pageheight} are dimension parameters, +initialized to $0$ by the generic \TeX\ code. + +@<Generate all \Prote\ primitives@>= +if (!pdf_on) { +primitive("pdfpagewidth", assign_dimen, dimen_base+page_width_code);@/ +@!@:pdf\_page\_width\_}{\.{\\pdfpagewidth} primitive@> +primitive("pdfpageheight", assign_dimen, dimen_base+page_height_code);@/ +@!@:pdf\_page\_height\_}{\.{\\pdfpageheight} primitive@> +} else { +primitive("pagewidth", assign_dimen, dimen_base+page_width_code);@/ +@!@:pagewidth\_}{\.{\\pagewidth} primitive@> +primitive("pageheight", assign_dimen, dimen_base+page_height_code);@/ +@!@:pageheight\_}{\.{\\pageheight} primitive@> +} + + + +@ When instructed to, the \.{h} and \.{v} last values are transformed, +in the coordinates system defined above and saved in the global +variables |last_saved_xpos| and |last_saved_ypos|. They are +initialized to $0$ and we do not make any verification that a +call to the \.{\\savepos} primitive---to come---has been made before +retrieving their values. + +@<Global...@>= +static scaled @!last_saved_xpos, last_saved_ypos; /*last (x,y) DVI pos saved*/ + +@ @<\Prote\ initializations@>= +last_saved_xpos=0;last_saved_ypos=0; + +@ @<Set |last_saved_xpos| and |last_saved_ypos| with transformed coordinates@>= +last_saved_xpos=cur_h+DVI_std_x_offset; +last_saved_ypos=page_height-(cur_v+DVI_std_y_offset); + +@ @<Generate all \Prote\ primitives@>= +if (!pdf_on) { +primitive("pdflastxpos", last_item, last_xpos_code);@/ +@!@:pdflastxpos\_}{\.{\\pdflastxpos} primitive@> +primitive("lastypos", last_item, last_ypos_code);@/ +@!@:pdflastypos\_}{\.{\\pdflastypos} primitive@> +} else { +primitive("lastxpos", last_item, last_xpos_code);@/ +@!@:lastxpos\_}{\.{\\lastxpos} primitive@> +primitive("lastypos", last_item, last_ypos_code);@/ +@!@:lastypos\_}{\.{\\lastypos} primitive@> +} + +@ @<Cases of |last_item| for |print_cmd_chr|@>= +case last_xpos_code: + if (pdf_on) print_esc("pdflastxpos"); + else print_esc("lastxpos"); + @+break; +case last_ypos_code: + if (pdf_on) print_esc("pdflastypos"); + else print_esc("lastypos"); + @+break; + +@ @<Cases for fetching a \Prote\ int value@>= +case last_xpos_code: cur_val=last_saved_xpos;@+break; +case last_ypos_code: cur_val=last_saved_ypos; + +@ |last_saved_xpos| and |last_saved_ypos| are only defined when +instructed to by the call the the \.{\\savepos} primitive. Since the +real work has to be done at \.{shipout} time, it is a case to be treated +like the \.{\\special} primitive, that is it belongs to the +\.{extension} class. + +We will add something more in the handling of the primitive: it will +insert a \.{whatsit} in the DVI file so that one, using the program +|dvitype|, could retrieve more than one {\it hic}. So there is a counter +incremented whenever the primitive is called. + +@<Global...@>= +static int last_save_pos_number; /*identifying the order of the call*/ + +@ @<\Prote\ initializations@>= +last_save_pos_number=0; /*i.e. none*/ + +@ @<Generate all \Prote\ primitives@>= +if (pdf_on) +primitive("pdfsavepos", extension, save_pos_code);@/ +@!@:pdf\_save\_pos\_}{\.{\\pdfsavepos} primitive@> +else +primitive("savepos", extension, save_pos_code);@/ +@!@:savepos\_}{\.{\\savepos} primitive@> + + +@ @<Cases of |extension| for |print_cmd_chr|@>= +case save_pos_code: + if (pdf_on) print_esc("pdfsavepos"); + else print_esc("savepos"); + @+break; +@ @<Cases for |do_extension|@>= +case save_pos_code: @<Implement \.{\\savepos}@>@;@+break; + +@ We need the basic two words node, since we don't pass any parameter +and it is just an instruction to do something. So the \.{whatsit} node +is just the call. + +@<Implement \.{\\savepos}@>= +{@+new_whatsit(save_pos_code, small_node_size);write_stream(tail)=null; + write_tokens(tail)=null; + } + +@ @<Cases for displaying the |whatsit| node@>= +case save_pos_code: + if (pdf_on) print_esc("pdfsavepos"); + else print_esc("savepos"); + @+break; +@ @<Cases for making a partial copy of the whatsit node@>= +case save_pos_code: {@+r=get_node(small_node_size); + words=small_node_size; + } @+break; + +@ @<Cases for wiping out the whatsit node@>= +case save_pos_code: free_node(p, small_node_size);@+break; + +@ So, after these trivial initializations, what will we effectively do? +When the following procedure will be called, we define |last_saved_xpos|, +|last_saved_ypos|, increment |last_save_pos_number|, and a |warning| +followed by three +|key==value| space separated definitions as a \.{\\special}, the first +being prefixed by the string |__PROTE_| (shall be considered a reserved +prefix) and the string |SAVEPOS_|, equal to the index of the call, and +the |XPOS| and |YPOS| definitions. + +This is obviously, from the previous description, a variation around +|special_out|. + +@<Declare procedures needed in |out_what|@>= +static void save_pos_out(pointer @!p) +{@+int old_setting; /*holds print |selector|*/ +int @!k; /*index into |str_pool|*/ +synch_h;synch_v;incr(last_save_pos_number); +@<Set |last_saved_xpos| and |last_saved_ypos|...@>@; +old_setting=selector;selector=new_string; +print("warning __PROTE_");print("SAVEPOS");print_char('='); +print_int(last_save_pos_number);print_char(' '); +print("XPOS");print("=");print_int(last_saved_xpos);print_char(' '); +print("YPOS");print("=");print_int(last_saved_ypos); +selector=old_setting; +str_room(1); /*abort if probably overflowed and truncated*/ +dvi_out(xxx1);dvi_out(cur_length); /*it's less than 256*/ +for (k=str_start[str_ptr]; k<=pool_ptr-1; k++) dvi_out(so(str_pool[k])); +pool_ptr=str_start[str_ptr]; /*forget the not commited tentative string*/ +} + +@ @<Cases for |out_what|@>= +case save_pos_code: save_pos_out(p);@+break; + +@* System-dependent changes. +This section should be replaced, if necessary, by any special +modifications of the program +that are necessary to make \TeX\ work at a particular installation. +It is usually best to design your change file so that all changes to +previous sections preserve the section numbering; then everybody's version +will be consistent with the published program. More extensive changes, +which introduce new sections, can be inserted here; then only the index +itself will get a new section number. +@^system dependencies@> + +@* \TeX\ Live Integration. +A \TeX\ engine that aspires +to become a member of the \TeX\ Live family of programs +must + +$-$ respect the \TeX\ Live conventions for command line parameters,\par +$-$ find its input files using the \.{kpathsearch} library, and\par +$-$ implement \TeX\ primitives to support +\LaTeX. + +\noindent Naturally, the functions that follow here are taken, with small +modifications, from the \TeX\ Live sources. What is +added here, or rather subtracted here, are the parts that are specific +to some of the \TeX\ engines included in \TeX\ Live. New is also +that the code is presented in literate programming style. + +The code that follows is organized in three parts. +Some code for \TeX\ Live must come before +the definition of \TeX's macros because +it uses include files containing identifiers that are in conflict +with \TeX's macros or modify these macros. For example +\TeX's |banner| is modified by adding the \TeX\ Live version. + +@<Header files and function declarations@>= +#ifdef WEB2CVERSION +#define TL_VERSION "(TeX Live "WEB2CVERSION")" +#else +#define TL_VERSION +#endif + +@ The remaining two parts are first auxiliary variables and functions and then +those functions that are called from the ``classic'' \TeX\ code. + +@p @<\TeX\ Live auxiliary variables@>@; + @<\TeX\ Live auxiliary functions@>@; + @<\TeX\ Live functions@>@; + +@ +Most of the code that we present next comes together in the +function |main_init| +which is the first function called in the main program of a \TeX\ engine +belonging to \TeX\ Live. Before doing so, we make copies of +argument count and argument vector putting them in global variables. +@<Global...@>= +static char **argv; +static int argc; + +@ @<\TeX\ Live functions@>= +static void main_init(int ac, char *av[]) +{@+ + char* main_input_file; + argc = ac; + argv = av; + interaction = error_stop_mode; + kpse_record_input = recorder_record_input; + kpse_record_output = recorder_record_output; + + @<parse options@>@; + @<set the program and engine name@>@; + @<activate configuration lines@>@; + @<set the input file name@>@; + @<set defaults from the {\tt texmf.cfg} file@>@; + @<set the format name@>@; + @<enable the generation of input files@>@; +} + +@ @<Forward declarations@>= +static void main_init(int ac, char *av[]); + + +@*1 Command Line. + Let's begin with the beginning: the command line. +To see how a command line is structured, we first look at the +help text that is displayed if the user asks for it (or if \TeX\ +decides that the user needs it). The help text is produced by the +function |usage_help|. +\def\SP{ }% disable visible spaces in strings. + +@<\TeX\ Live auxiliary functions@>= +static void usage_help(void) +{@+@<explain the command line@>@; + fprintf(stdout,"Options:\n"@,@[@<explain the options@>@]); + fprintf(stdout,"\nFor further information and reporting bugs see https://hint.userweb.mwn.de/\n"); + exit(0); +} + +@ The command line comes in three slightly different versions: + +@<explain the command line@>= + fprintf(stdout,@/ + "Usage: %s [OPTION]... [TEXNAME[.tex]] [COMMANDS]\n"@/ + " or: %s [OPTION]... \\FIRST-LINE\n"@/ + " or: %s [OPTION]... &FMT ARGS\n\n",@/ + argv[0],argv[0],argv[0]);@/ + fprintf(stdout,@/ + " Run TeX on TEXNAME, creating TEXNAME.dvi.\n"@/ + " Any remaining COMMANDS are processed\n"@/ + " as TeX input after TEXNAME is read.\n"@/ + " If the first line of TEXNAME starts with %%&FMT, and FMT is\n"@/ + " an existing .fmt file, use it. Else use `NAME.fmt', where\n"@/ + " NAME is the program invocation name.\n"@/ + "\n"@/ + " Alternatively, if the first non-option argument begins\n"@/ + " with a backslash, interpret all non-option arguments as\n"@/ + " a line of TeX input.\n"@/ + "\n"@/ + " Alternatively, if the first non-option argument begins\n"@/ + " with a &, the next word is taken as the FMT to read,\n"@/ + " overriding all else. Any remaining arguments are\n"@/ + " processed as above.\n"@/ + "\n"@/ + " If no arguments or options are specified, prompt for input.\n"@/ + "\n"); + + +@*1 Options. +Here is the list of possible options and their explanation: + +@<explain the options@>= + " -help "@/ + @t\qquad@>"\t display this help and exit\n"@/ + " -version "@/ + @t\qquad@>"\t output version information and exit\n"@/ + " -etex "@/ + @t\qquad@>"\t enable e-TeX extensions\n"@/ + " -ltx "@/ + @t\qquad@>"\t enable LaTeX extensions, implies -etex\n"@/ + " -ini "@/ + @t\qquad@>"\t be initex for dumping formats; this is\n"@/ + @t\qquad@>"\t\t\t also true if the program name is `kinitex'\n"@/ + " -progname=STRING "@/ + @t\qquad@>"\t set program (and fmt) name to STRING\n"@/ + " -fmt=FMTNAME "@/ + @t\qquad@>"\t use FMTNAME instead of program name or a %%& line\n"@/ + " -output-directory=DIR "@/ + @t\qquad@>"\t use existing DIR as the directory to write files to\n"@/ + " -jobname=STRING "@/ + @t\qquad@>"\t set the TeX \\jobname to STRING\n"@/ + " [-no]-mktex=FMT "@/ + @t\qquad@>"\t disable/enable mktexFMT generation (FMT=tex/tfm/fmt)\n"@/ + " -interaction=STRING "@/ + @t\qquad@>"\t set interaction mode (STRING=batchmode/\n"@/ + @t\qquad@>"\t\t\t nonstopmode/scrollmode/errorstopmode)\n"@/ + " -kpathsea-debug=NUMBER"@/ + @t\qquad@>"\t set path searching debugging flags according\n"@/ + @t\qquad@>"\t\t\t to the bits of NUMBER\n"@/ + " -recorder"@/ + @t\qquad@>"\t\t enable filename recorder\n"@/ + " [-no]-parse-first-line"@/ + @t\qquad@>"\t disable/enable parsing of the first line of\n"@/ + @t\qquad@>"\t\t\t the input file\n"@/ + " [-no]-file-line-error"@/ + @t\qquad@>"\t disable/enable file:line:error style\n"@/ + " -cnf-line=STRING"@/ + @t\qquad@>"\t process STRING like a line in texmf.cnf\n"@/ + + +@ The processing of command line options is controlled by the |long_options| +\def\SP{{\tt\char`\ }}% restore the visible space in a string +array. Each entry in this array contains first the name of the option, +then a flag that tells whether the option takes an argument or not. +If next the (optional) address of a flag variable is given, +it is followed by the value to store in the flag variable. +In this case, setting the flag variable is handled by +the |getopt_long_only| function. + +Besides the flag variables that occur in the table, +a few string variables may be set using the options. +The following is a complete list of these variables. +Variables are initialized with |-1| to indicate an undefined value; +string variables are initialized with |NULL|. + +@<Global variables@>= +static int iniversion=false, etexp=false, ltxp=false, recorder_enabled=false; +static int parsefirstlinep=-1, filelineerrorstylep=-1, interaction_option=-1; +static const char *user_progname=NULL, *output_directory=NULL, *c_job_name=NULL, + *dump_name=NULL;@# + +@ @<\TeX\ Live auxiliary variables@>= +static struct option long_options[] = {@/ + { "help", 0, 0, 0 },@/ + { "version", 0, 0, 0 },@/ + { "interaction", 1, 0, 0 },@/ + { "mktex", 1, 0, 0 },@/ + { "no-mktex", 1, 0, 0 },@/ + { "kpathsea-debug", 1, 0, 0 },@/ + { "progname", 1, 0, 0 },@/ + { "fmt", 1, 0, 0 },@/ + { "output-directory", 1, 0, 0 },@/ + { "jobname", 1, 0, 0 },@/ + { "cnf-line", 1, 0, 0 },@/ + { "ini", 0, &iniversion, 1 },@/ + { "etex", 0, &etexp, 1 },@/ + { "ltx", 0, <xp, 1 },@/ + { "recorder", 0, &recorder_enabled, 1 },@/ + { "parse-first-line", 0, &parsefirstlinep, 1 },@/ + { "no-parse-first-line", 0, &parsefirstlinep, 0 },@/ + { "file-line-error", 0, &filelineerrorstylep, 1 },@/ + { "no-file-line-error", 0, &filelineerrorstylep, 0 },@/ + @<more options@>@/ + { 0, 0, 0, 0 }@+}@+; + + +@ Parsing the command line options is accomplished with the +|parse_options| function which in turn uses the |getopt_long_only| +function from the \CEE/ library. This function returns 0 and sets the +|option_index| parameter to the option found, or it returns $-1$ if +the end of all options is reached. +@<\TeX\ Live functions@>= +static void parse_options (int argc, char *argv[]) +{@+ while (true) { + int option_index; + int g = getopt_long_only (argc, argv, "+", long_options, &option_index); + if (g==0) + { @<handle the options@>@;@+ } + else if (g == '?') + { fprintf(stderr,"Try '%s --help' for more information\n",argv[0]); + exit(1); + } + else if (g == -1) break; + } + @<Check the environment for extra settings@>@; +} + +@ @<Forward declarations@>= +static void parse_options (int argc, char *argv[]); + +@ Before we can call the |parse_options| function, +we might need some special preparations for Windows. + +@<parse options@>= +#if defined(WIN32) +{@+ char* enc; + kpse_set_program_name (argv[0], NULL); + enc = kpse_var_value("command_line_encoding"); + get_command_line_args_utf8(enc, &argc, &argv);@/ + parse_options (argc, argv); + @<record {\tt texmf.cnf}@>@; +} +#else + parse_options (ac, av); +#endif + + +@ To handle the options, we compare the name at the given |option_index| with +the different option names. This is not a very efficient method, but the +impact is low and it's simple to write. + +Comparing the name of the argument with the |name| field in the |option| +structure is done in the auxiliary function |argument_is|. +Unfortunately the |name| field is in conflict with the |name| macro +defined by \TeX. To avoid the conflict, the |argument_is| function +goes just after the \.{kpathsea.h} header file that defines +the option structure. + + +@<Header files and function declarations@>= +#include <kpathsea/kpathsea.h> +static int argument_is(struct option *opt, char * s) +{@+ return STREQ(opt->name, s); @+} +#define ARGUMENT_IS(S) argument_is(long_options+option_index,S) + +@ Now we can handle the first two options: + +@<handle the options@>= +if (ARGUMENT_IS("help")) usage_help(); +else if (ARGUMENT_IS("version")){@+ + printf(TeX_banner@, "\n"@/ + "Prote version "@, Prote_version_string@, "\n"); + exit(0);@+ +} + + +@ The ``interaction'' option sets the |interaction_option| variable +based on its string argument contained in the |optarg| variable. +If defined, the |interaction_option| will be used to set \TeX's +|interaction| variable in the |initialize| and the |undump| functions. + +@<handle the options@>= +else @+if (ARGUMENT_IS ("interaction"))@t\2@> { + if (STREQ (optarg, "batchmode")) interaction_option = batch_mode; + else if (STREQ (optarg, "nonstopmode")) interaction_option = nonstop_mode; + else if (STREQ (optarg, "scrollmode")) interaction_option = scroll_mode; + else if (STREQ (optarg, "errorstopmode")) interaction_option = error_stop_mode; + else WARNING1 ("Ignoring unknown argument `%s' to --interaction", optarg); + } + +@ The next two options pass the string argument to the +\.{kpathsearch} library. + +@<handle the options@>= +else @+if (ARGUMENT_IS ("mktex")) @t\2@> kpse_maketex_option (optarg, true); +else if (ARGUMENT_IS ("no-mktex")) kpse_maketex_option (optarg, false); + + +@ To debug the searching done by the \.{kpathsearch} library, +the following option can be used. +The argument value 3 is a good choice to start with. + +@<handle the options@>= +else @+if (ARGUMENT_IS ("kpathsea-debug")) @t\2@> + kpathsea_debug |= atoi (optarg); + + + +@ The next set of options take a string argument +and assign it to the corresponding string variable. + +@<handle the options@>= +else @+if (ARGUMENT_IS ("progname"))@t\2@> + user_progname = normalize_quotes(optarg,"program name"); +else if (ARGUMENT_IS ("fmt")) + dump_name = normalize_quotes(optarg,"format name"); +else if (ARGUMENT_IS ("output-directory")) + output_directory = normalize_quotes(optarg,"output directory"); +else if (ARGUMENT_IS ("jobname")) + c_job_name = normalize_quotes (optarg, "job name"); + +@ When string arguments specify files or directories, +special care is needed if arguments are quoted and/or contain spaces. +The function |normalize_quotes| makes sure that arguments containing +spaces get quotes around them and it checks for unbalanced quotes. + + +@<\TeX\ Live auxiliary functions@>= +static char *normalize_quotes (const char *nom, const char *mesg) +{@+ int quoted = false; + int must_quote = (strchr(nom, ' ') != NULL); + char *ret = xmalloc(strlen(nom)+3);/*room for two quotes and NUL*/ + char *p=ret; + const char *q; + + if (must_quote) + *p++ = '"'; + for (q = nom; *q; q++) + if (*q == '"') + quoted = !quoted;@+ + else + *p++ = *q; + + if (must_quote) + *p++ = '"'; + *p = '\0'; + if (quoted) { + fprintf(stderr, "! Unbalanced quotes in %s %s\n", mesg, nom); + exit(1); + } + return ret; +} + +@ If the output directory was specified on the command line, +we save it in an environment variable so that subbrocesses can +get the value. If on the other hand the environment specifies +a directory and the command line does not, save the value from +the environment to the global variable so that it is used in the +rest of the code. + +@<Check the environment for extra settings@>= +if (output_directory) + xputenv ("TEXMF_OUTPUT_DIRECTORY", output_directory); +else if (getenv ("TEXMF_OUTPUT_DIRECTORY")) + output_directory = getenv ("TEXMF_OUTPUT_DIRECTORY"); + +@*1 Passing a file name as a general text argument. + +|scan_file_name| uses the following code to parse a file name given +as a general text argument. Such an argument can be any token list +starting with a left brace and ending with a right brace. +This token list is then expanded (without the leading and trailing braces) +and printed into the string pool without making it yet an official string. +After removing all double quotes, because this is current practice +for \TeX\ engines that are part of \TeX\ Live, +and setting the area and extension delimiters, +all temporary garbage used so far is freed. + +Due to the expansion of the token list, this code and hence the +|scan_file_name| procedure is recursive. +One can provide the name of a file as the content of an other file. + + +% Thierry Laronde +@<Define a general text file name and |goto done|@>= +{@+back_input();name_in_progress=false; /*this version is recursive...*/ + cur_cs=input_loc; /*|scan_toks| will set |warning_index| from it*/ + scan_general_x_text(); + old_setting=selector;selector=new_string; + token_show(link(garbage));selector=old_setting; + @<Suppress double quotes in braced input file name@>@; + j=pool_ptr-1;while ((j >= str_start[str_ptr])&&(area_delimiter==0)) + {@+ + if ((str_pool[j]=='/')) area_delimiter=j-str_start[str_ptr]; + if ((ext_delimiter==0)&&(str_pool[j]=='.')) + ext_delimiter=j-str_start[str_ptr]; + decr(j); + } + flush_list(link(garbage)); + goto done; +} + +@ A simple loop removes the double quotes and adjusts the |pool_ptr|. + +@<Suppress double quotes in braced input file name@>= +for (k=j=str_start[str_ptr]; k < pool_ptr; k++) {@+ + if (str_pool[k]!='"') {@+str_pool[j]=str_pool[k];incr(j);} +} +pool_ptr=j; + +@*1 The {\tt -recorder} Option. +The recorder option can be used to enable the file name recorder. +It is crucial for getting a reliable list of files used in a given run. +Many post-processors use it, and it is used in \TeX\ Live for +checking the format building infrastructure. + +When we start the file name recorder, +we would like to use mkstemp, but it is not portable, +and doing the autoconfiscation (and providing fallbacks) is more +than we want to cope with. So we have to be content with using a +default name. We throw in the pid so at least parallel builds might +work. Windows, however, seems to have no |pid_t|, so instead of storing the +value returned by |getpid|, we immediately consume it. + +@<\TeX\ Live auxiliary functions@>= +static char *recorder_name=NULL; +static FILE *recorder_file=NULL; +static void +recorder_start(void) +{ char *cwd; + char pid_str[MAX_INT_LENGTH]; + sprintf (pid_str, "%ld", (long) getpid()); + recorder_name = concat3(kpse_program_name, pid_str, ".fls"); + if (output_directory) { + char *temp = concat3(output_directory, DIR_SEP_STRING, recorder_name); + free(recorder_name); + recorder_name = temp; + } + recorder_file = xfopen(recorder_name, FOPEN_W_MODE); + cwd = xgetcwd(); + fprintf(recorder_file, "PWD %s\n", cwd); + free(cwd); +} + +@ After we know the log file name, we have used |recorder_change_filename| +to change the name of the recorder file to the usual thing. +@<Forward declarations@>= +static void recorder_change_filename (const char *new_name); + +@ Now its time to define this function. +Unfortunately, we have to explicitly take +the output directory into account, since the new name we are +called with does not; it is just the log file name with {\tt .log} +replaced by {\tt .fls}. + +@ @<\TeX\ Live auxiliary functions@>= +static void +recorder_change_filename (const char *new_name) +{ char *temp = NULL; + if (!recorder_file) + return; +#if defined(_WIN32) + fclose (recorder_file); /* An open file cannot be renamed. */ +#endif /* |_WIN32| */ + if (output_directory) { + temp = concat3(output_directory, DIR_SEP_STRING, new_name); + new_name = temp; + } + +#if defined(_WIN32) + remove (new_name); /* A file with the |new_name| must not exist. */ +#endif /* |_WIN32| */ + rename(recorder_name, new_name); + free(recorder_name); + recorder_name = xstrdup(new_name); +#if defined(_WIN32) + recorder_file = xfopen (recorder_name, FOPEN_A_MODE); /* A closed file must be opened. */ +#endif /* |_WIN32| */ + if (temp) + free (temp); +} + +@ Now we are ready to record file names. The prefix INPUT is added +to an input file and the prefix OUTPUT to an output file. +But both functions for recording a file name use the same +function otherwise, which on first use will start the recorder. + +@<\TeX\ Live auxiliary functions@>= +static void +recorder_record_name (const char *pfx, const char *fname) +{ @+if (recorder_enabled) { + if (!recorder_file) + recorder_start(); + fprintf(recorder_file, "%s %s\n", pfx, fname); + fflush(recorder_file); + } +} + + +static void +recorder_record_input (const char *fname) +{ recorder_record_name ("INPUT", fname); +} + +static void +recorder_record_output (const char * fname) +{ recorder_record_name ("OUTPUT", fname); +} + +@ Because input files are also recorded when writing the optional sections, +we need the following declaration. + +@<Forward declarations@>= +static void recorder_record_input (const char *fname); + +@ In WIN32, texmf.cnf is not recorded in +the case of {\tt -recorder}, because |parse_options| is executed +after the start of kpathsea due to special initializations. +Therefore we record {\tt texmf.cnf} with the following code: + +@<record {\tt texmf.cnf}@>= +if (recorder_enabled) { + char **p = kpse_find_file_generic ("texmf.cnf", kpse_cnf_format, 0, 1); + if (p && *p) { + char **pp = p; + while (*p) { + recorder_record_input (*p); + free (*p); + p++; + } + free (pp); + } +} + + + + +@*1 The {\tt -cnf-line} Option. +With the \.{-cnf-line} option it is possible to specify +a line of text as if this line were part of \TeX's configuration +file---even taking precedence over conflicting lines in the configuration +file. For example it is possible to change \TeX's +\.{TEXINPUTS} variable by saying \.{--cnf-line=TEXINPUTS=/foo}. +The configuration lines are temporarily stored in the variable +|cnf_lines| and counted in |cnf_count| because we can send them +to the \.{kpathsearch} library only after the library has been +initialized sufficiently. + +@<Global...@>= +static char **cnf_lines=NULL; +static int cnf_count=0; + +@ @<handle the options@>= +else @+if (ARGUMENT_IS ("cnf-line")) + add_cnf_line(optarg); + +@ The function |add_cnf_line| stores the +given command line argument in the variable |cnf_lines|. + +@<\TeX\ Live auxiliary functions@>= +static void add_cnf_line(char *arg) +{@+ cnf_count++; + cnf_lines=xrealloc(cnf_lines,sizeof(char*)*cnf_count); + cnf_lines[cnf_count-1]=arg; +} + +@ To activate the configuration lines they are passed to the + \.{kpathsearch} library. + +@<activate configuration lines@>= +#if 1 /* this function does not exists always */ +{@+ int i; + for (i=0;i<cnf_count;i++) + kpathsea_cnf_line_env_progname (kpse_def, cnf_lines[i]); + free(cnf_lines); +} +#endif + + +@*1 The Input File. +After we are done with the options, +we inform the \.{kpathsearch} library about the program name. +This is an important piece of information for the library +because the library serves quite different programs +and its behavior can be customized for each program +using configuration files. +After the program and engine name is set, +the library is ready to use. +@<set the program and engine name@>= +if (!user_progname) + user_progname = dump_name; +#if defined(WIN32) + if (user_progname) + kpse_reset_program_name (user_progname); +#else + kpse_set_program_name (argv[0], user_progname); +#endif + xputenv ("engine", "texprof");@/ + + +@ After the options, the command line usually continues with the name of the input +file. Getting a hold of the input file name can be quite complicated, +but the \.{kpathsearch} library will help us to do the job. + +We start by looking at the first argument after the options: +If it does not start with an +``\.{\AM}'' and neither with a ``\.{\BS}'', it's a simple file name. +Under Windows, however, filenames might start with a drive letter +followed by a colon and a ``\.{\BS}'' which is used to separate +directory names. Finally, if the filename is a quoted string, we need +to remove the quotes before we use the \.{kpathsearch} library to find it +and reattach the quotes afterward. + +@<\TeX\ Live auxiliary functions@>= +#ifdef WIN32 +static void clean_windows_filename(char *filename) +{@+if (strlen (filename) > 2 && isalpha (filename[0]) && + filename[1] == ':' && filename[2] == '\\') + { char *pp; + for (pp = filename; *pp; pp++) + if (*pp == '\\') + *pp = '/'; + } +} +#endif + +static char *find_file(char *fname, kpse_file_format_type t, int mx) +{@+char *filename; + int final_quote=(int)strlen(fname)-1; + int quoted = final_quote>1 && fname[0] == '"' && fname[final_quote]=='"'; + if (quoted) { + /* Overwrite last quote and skip first quote. */ + fname[final_quote] = '\0'; + fname++; + } + filename = kpse_find_file(fname, t, mx); + if (full_name_of_file!=NULL) + { free(full_name_of_file); full_name_of_file=NULL;} + if (filename!=NULL) + full_name_of_file=strdup(filename); + if (quoted) { + /* Undo modifications */ + fname--; + fname[final_quote] = '"'; + } + return filename; +} + +static char *get_input_file_name (void) +{@+ + char *input_file_name = NULL; + + if (argv[optind] && argv[optind][0] != '&' && argv[optind][0] != '\\') + { +#ifdef WIN32 + clean_windows_filename(argv[optind]); +#endif + argv[optind] = normalize_quotes(argv[optind], "input file"); + input_file_name=find_file(argv[optind],kpse_tex_format, false); + } + return input_file_name; +} + + +@ After we called |get_input_file_name|, we might need to look at |argv[argc-1]| +in case we run under Windows. + +@<set the input file name@>= + main_input_file = get_input_file_name (); + +#ifdef WIN32 + /* Were we given a simple filename? */ + if (main_input_file == NULL) { + char *file_name=argv[argc-1]; + if (file_name && file_name[0] != '-' && file_name[0] != '&' && file_name[0] != '\\') { + clean_windows_filename(file_name); + file_name = normalize_quotes(file_name, "argument"); + main_input_file=find_file(file_name,kpse_tex_format, false); + argv[argc-1] = file_name; + } + } +#endif + + +@ After we have an input file, we make an attempt at filling in options +from the {\tt texmf.cfg} file. + +@<set defaults from the {\tt texmf.cfg} file@>= + if (filelineerrorstylep < 0) + filelineerrorstylep = texmf_yesno ("file_line_error_style"); + if (parsefirstlinep < 0) + parsefirstlinep = texmf_yesno ("parse_first_line");@/@t~@> + +@ We needed: +@<\TeX\ Live auxiliary functions@>= +static int texmf_yesno(const char *var) +{@+ + char *value = kpse_var_value (var); + return value && (*value == 't' || *value == 'y' || *value == '1'); +} + +@ We need a stack, matching the |line_stack| that +contains the source file names. For the full source filenames we use +poiters to |char| because these names are just used for output. + +@<Global...@>= +static char * @!source_filename_stack0[max_in_open]={NULL}, **const @!source_filename_stack = @!source_filename_stack0-1; +static char * @!full_source_filename_stack0[max_in_open]={NULL}, **const @!full_source_filename_stack = @!full_source_filename_stack0-1; +static char *full_name_of_file=NULL; + +@ The function |print_file_line| +prints ``file:line:error'' style messages using +the |source_filename_stack|. If it fails to find the file name, it +falls back to the ``non-file:line:error'' style. + +@<Basic printing...@>= +static void print_file_line(void) +{@+int level=in_open; + while (level>0 && full_source_filename_stack[level]==NULL) level--; + if (level==0) print_nl("! "); + else + { print_nl(""); print(full_source_filename_stack[level]); print_char(':'); + if (level==in_open) print_int(line); + else print_int(line_stack[level]); + print(": "); + } +} + +@*1 The Format File. +Most of the time \TeX\ is not running as \.{initex} +but it runs with a format file preloaded. +To set the format name, we first check if the format name was given on the +command line with an ``\.{\AM}'' prefix, +second we might check the first line of the input file, +and last, we check if the program is an initex program. + +If we still don't have a format, the program +name is our best guess. +There is no need to check for an extension, because the +\.{kpathsearch} library will take care of that. +We store the format file name in |dump_name| which is +used in the function |w_open_in| below. + +@<set the format name@>= +if (parsefirstlinep && !dump_name) + parse_first_line (main_input_file); +if (!main_input_file && argv[1] && argv[1][0] == '&') + dump_name = argv[1] + 1; +if (strcmp(kpse_program_name, "initexprof")==0) iniversion = true; +if (!dump_name) + dump_name = kpse_program_name; +if (!dump_name) +{ fprintf(stderr,"Unable to determine format name\n"); + exit(1); +} +if (ltxp) etexp=1; +if (etexp && !iniversion) +{ fprintf(stderr,"-etex and -ltx require -ini\n"); + exit(1); +} + +@ Here is the function |parse_first_line |. It searches the first line of +the file for a \TeX\ comment of the form ``\%\AM format''\footnote{${}^1$}{The idea of using this format came from Wlodzimierz Bzyl.}. +If found, we will use the format given there. + +@<\TeX\ Live auxiliary functions@>= +static void parse_first_line(char*filename) +{@+ FILE *f=NULL; + if (filename==NULL) return; + f = open_in(filename,kpse_tex_format,"r"); + if (f!=NULL) + { char *r,*s,*t=read_line (f); + xfclose (f, filename); + if (t==NULL) return; + s=t; + if (s[0] == '%' && s[1] == '&') { + s = s+2; + while ( ISBLANK(*s)) ++s; + r=s; + while (*s!=0 && *s!=' ' && *s!='\r' && *s!='\n') s++; + *s=0; + if (dump_name==NULL) { + char *f_name = concat (r, format_extension); + char *d_name = kpse_find_file (f_name,kpse_fmt_format, false); + if (d_name && kpse_readable_file (d_name)) { + dump_name = xstrdup (r); + kpse_reset_program_name (dump_name); + } + free (f_name); + } + } + free (t); + } +} + +@*1 Commands. +In the old days, \TeX\ was a \PASCAL\ program, and standard \PASCAL\ +did say nothing about a command line. So \TeX\ would open the terminal +file for input and read all the information from the terminal. +If you don't give \TeX\ command line arguments, this is still true today. +In our present time, people got so much used to control the behavior +of a program using command line arguments---especially when writing +scripts---that \TeX\ Live allows the specification of commands on +the command line which \TeX\ would normally expect on +the first line of its terminal input. + +So our next task is +writing a function to add the remainder of the +command line to \TeX's input buffer. +The main job is done by the |input_add_str| function +which duplicates part of the |input_ln| function. +Further it skips initial spaces and replaces trailing spaces +and line endings by a single space. + +@<\TeX\ Live auxiliary functions@>= +static void input_add_char(unsigned int c) +{@+ if (last >= max_buf_stack) + {@+max_buf_stack=last+1; + if (max_buf_stack==buf_size) + @<Report overflow of the input buffer, and abort@>; + } + buffer[last]=xord[c];incr(last); +} + +static void input_add_str(const char *str) +{@+ int prev_last; + while (*str==' ') str++; + prev_last=last; + while (*str!=0) + +input_add_char(*str++); + for (--last; last >= first ; --last) + { char c =buffer[last]; + if ((c) != ' ' && (c) != '\r' && (c) != '\n') break; + } + last++; + if (last>prev_last) input_add_char(' '); +} + +static int input_command_line(void) +{@+ +last=first; +while (optind < argc) input_add_str(argv[optind++]); +loc=first; +return (loc < last); +@+ } + +@ @<Forward declarations@>= +static int input_command_line(void); + + +@*1 Opening Files. +When we open an output file, there is usually no searching necessary. +In the best case, we have an absolute path and can open it. +If the path is relative, we try in this order: +the |file_name| prefixed by the |output_directory|, +the |file_name| as is, and +the |file_name| prefixed with the environment variable |TEXMFOUTPUT|. + +If we were successful with one of the modified names, we update +|name_of_file|. + +@<\TeX\ Live functions@>= +static FILE *open_out(const char *file_name, const char *file_mode) +{@+ FILE *f=NULL; + char *new_name=NULL; + int absolute = kpse_absolute_p(file_name, false); + if (absolute) + { f=fopen(file_name,file_mode); + if (f!=NULL) recorder_record_output (file_name); + return f; + } + if (output_directory) + { new_name = concat3(output_directory, DIR_SEP_STRING, file_name); + f = fopen(new_name,file_mode); + if (f==NULL) {@+ free(new_name); new_name=NULL;@+} + } + if (f==NULL) + f = fopen(file_name,file_mode); + if (f==NULL) + { const char *texmfoutput = kpse_var_value("TEXMFOUTPUT"); + if (texmfoutput!=NULL && texmfoutput[0]!=0) + { new_name = concat3(texmfoutput, DIR_SEP_STRING, file_name);@/ + f = fopen(new_name,file_mode); + if (f==NULL) {@+ free(new_name); new_name=NULL; @+} + } + } + if (f!=NULL && new_name!=NULL) + update_name_of_file(new_name,(int)strlen(new_name)); + if (f!=NULL) recorder_record_output ((char*)name_of_file+1); + if (new_name!=NULL) free(new_name); + return f; +} + +static bool a_open_out(alpha_file *f) /*open a text file for output*/ +{@+f->f=open_out((char*)name_of_file+1,"w"); + return f->f!=NULL && ferror(f->f)==0;@+ +} + +static bool b_open_out(byte_file *f) /*open a binary file for output*/ +{@+f->f=open_out((char *)name_of_file+1,"wb"); +return f->f!=NULL && ferror(f->f)==0;@+ +} + +#ifdef @!INIT +static bool w_open_out(word_file *f) /*open a word file for output*/ +{@+f->f=open_out((char *)name_of_file+1,"wb"); + return f->f!=NULL && ferror(f->f)==0;@+ +} +#endif + + +@ Format file names must be scanned +before \TeX's string mechanism has been initialized. +The function |update_name_of_file| will set |name_of_file| from +a \CEE/ string. + +We dare not give error messages here, since \TeX\ calls this routine before +the |error| routine is ready to roll. Instead, we simply drop excess characters, +since the error will be detected in another way when a strange file name +isn't found. +@^system dependencies@> + +@<\TeX\ Live auxiliary functions@>= +static void update_name_of_file(const char *@!s, int @!k) +{@+ int j; + if (k <= file_name_size) name_length=k;@+ + else name_length=file_name_size; + for (j=0; j<name_length;j++) name_of_file[j+1]=xchr[(int)s[j]]; + name_of_file[name_length+1]=0; +} + +@ In standard \TeX, the |reset| macro is used to open input files. +The \.{kpathsearch} library uses different search paths for +different types of files and therefore different functions are needed to +open these files. The common code is in the function |open_in|. + +@<\TeX\ Live auxiliary functions@>= +static FILE*open_in(char*filename,kpse_file_format_type t,const char*rwb) +{@+ char *fname=NULL; + FILE*f= NULL; + fname= find_file(filename,t,true); + if(fname!=NULL) + {@+ + f= fopen(fname,rwb); + if (f!=NULL) recorder_record_input(fname); + if (full_name_of_file!=NULL) free(full_name_of_file); + full_name_of_file=fname;@+ + } + return f; +} + +static bool a_open_in(alpha_file *f) /*open a text file for input*/ +{@+f->f= open_in((char *)name_of_file+1,kpse_tex_format,"r"); + if (f->f!=NULL) get(*f); + return f->f!=NULL && ferror(f->f)==0; +} + +static bool b_open_in(byte_file *f) /*open a binary file for input*/ +{@+f->f= open_in((char *)name_of_file+1,kpse_tfm_format,"rb"); + if (f->f!=NULL) get(*f); + return f->f!=NULL && ferror(f->f)==0; +} + +static bool w_open_in(word_file *f) /*open a word file for input*/ +{@+ + f->f=NULL; + if (name_of_file[1]!=0) + f->f= open_in((char*)name_of_file+1,kpse_fmt_format,"rb"); + if (f->f!=NULL) get(*f); + return f->f!=NULL && ferror(f->f)==0; +} + +@ \TeX's |open_fmt_file| function will call the following function +either with the name of a format file as given with an ``\.{\AM}'' +prefix in the input or with |NULL| if no such name was specified. +The function will try |dump_name| as a last resort before returning |NULL|. + + + +@<\TeX\ Live functions@>= +static bool open_fmt_file(void) +{@+int j=loc; + if (buffer[loc]=='&') + {@+incr(loc);j=loc;buffer[last]=' '; + while (buffer[j]!=' ') incr(j); + update_name_of_file((char *)buffer+loc,j-loc); + if (w_open_in(&fmt_file)) goto found; + } + update_name_of_file(dump_name,(int)strlen(dump_name)); + if (w_open_in(&fmt_file)) goto found; + name_of_file[1]=0; + wake_up_terminal; + wterm_ln("I can't find a format file!"); + return false; +found: loc=j;return true; +} + + +@ The \TeX\ Live infrastructure is able to generate format files, +font metric files, +and even some tex files, if required. + +@<enable the generation of input files@>= + kpse_set_program_enabled (kpse_tfm_format, MAKE_TEX_TFM_BY_DEFAULT, + kpse_src_compile);@/ + kpse_set_program_enabled (kpse_tex_format, MAKE_TEX_TEX_BY_DEFAULT, + kpse_src_compile);@/ + kpse_set_program_enabled (kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT, + kpse_src_compile);@/ + + + +@*1 Date and Time. +We conclude this chapter using \.{time.h} to provide a function +that is used to initialize +\TeX's date and time information. Because |time| is one of \TeX's +macros, we add the function |tl_now| before including \TeX's macros +to wrap the call to the |time| function. +It sets the variable |start_time| and returns a pointer to a |tm| structure +to be used later in |fix_date_and_time|. + +To support reproducible output, the environment variable |SOURCE_DATE_EPOCH| +needs to be checked. If it is set, it is an ASCII representation of +a UNIX timestamp, defined as the number +of seconds, excluding leap seconds, since 01 Jan 1970 00:00:00 UTC. +Its value is then used to initialize the |start_time| variable. + +The \TeX\ Live conventions further require that setting the +|FORCE_SOURCE_DATE| environment variable to $1$ will cause also \TeX's primitives +\.{\\year}, \.{\\month}, \.{\\day}, and \.{\\time} to use this value +as the current time. Looking at the \TeX\ Live code also reveals that +these primitives use the local time instead of the GMT if this variable is +not set to~1. + +@<Header files and function declarations@>= +#include <time.h> +static time_t start_time = ((time_t)-1); +static char *source_date_epoch,*force_source_date; + +#if defined(_MSC_VER) && _MSC_VER < 1800 +#define strtoull _strtoui64 +#endif + +static struct tm *tl_now(void) +{@+struct tm *tp; + time_t t; + source_date_epoch= getenv("SOURCE_DATE_EPOCH"); + force_source_date= getenv("FORCE_SOURCE_DATE"); + if (force_source_date!=NULL && + (force_source_date[0]!='1' || force_source_date[1]!=0)) + force_source_date=NULL; + + if (source_date_epoch!=NULL) + { start_time= (time_t)strtoull(source_date_epoch, NULL, 10); + if (force_source_date!=NULL) + t=start_time; + else + t=time(NULL); + } + else + t=start_time=time(NULL); + if (force_source_date) + tp=gmtime(&t); + else + tp=localtime(&t); + return tp; +} + +@*1 Retrieving File Properties. +To support \LaTeX, a few more time related functions are needed. +@<Header files and function declarations@>= +#define TIME_STR_SIZE 30 +static char time_str[TIME_STR_SIZE]; +static void get_creation_date(void); +static void get_file_mod_date(void); +static int get_file_size(void); + +#include <md5.h> +#define DIGEST_SIZE 16 +#define FILE_BUF_SIZE 1024 +static md5_byte_t md5_digest[DIGEST_SIZE]; + +static int get_md5_sum(int s, int file); + +@ The code that follows was taken from the \.{texmfmp.c} file of +the \TeX\ Live distribution and slightly modified. + + +@<\TeX\ Live auxiliary functions@>= + +static void +make_time_str(time_t t, bool utc) +{ + struct tm lt, gmt; + size_t size; + int off, off_hours, off_mins; + + /* get the time */ + if (utc) { + lt = *gmtime(&t); + } + else { + lt = *localtime(&t); + } + size = strftime(time_str, TIME_STR_SIZE, "D:%Y%m%d%H%M%S", <); + /* expected format: |"D:YYYYmmddHHMMSS"| */ + if (size == 0) { + /* unexpected, contents of |time_str| is undefined */ + time_str[0] = '\0'; + return; + } + + /* correction for seconds: S can be in range 00 to 61, + the PDF reference expects 00 to59, + therefore we map |"60"| and |"61"| to |"59"| */ + if (time_str[14] == '6') { + time_str[14] = '5'; + time_str[15] = '9'; + time_str[16] = '\0'; /* for safety */ + } + + /* get the time zone offset */ + gmt = *gmtime(&t); + + /* this calculation method was found in exim's tod.c */ + off = 60 * (lt.tm_hour - gmt.tm_hour) + lt.tm_min - gmt.tm_min; + if (lt.tm_year != gmt.tm_year) { + off += (lt.tm_year > gmt.tm_year) ? 1440 : -1440; + } else if (lt.tm_yday != gmt.tm_yday) { + off += (lt.tm_yday > gmt.tm_yday) ? 1440 : -1440; + } + + if (off == 0) { + time_str[size++] = 'Z'; + time_str[size] = 0; + } else { + off_hours = off / 60; + off_mins = abs(off - off_hours * 60); + snprintf(&time_str[size], TIME_STR_SIZE-size, "%+03d'%02d'", off_hours, off_mins); + } +} + +static void get_creation_date(void) +{ make_time_str(start_time,source_date_epoch!=NULL); +} + +/* static structure for file status set by |find_input_file| */ +#ifdef WIN32 +static struct _stat file_stat; +#define GET_FILE_STAT _stat(fname,&file_stat) +#else +static struct stat file_stat; +#define GET_FILE_STAT stat(fname,&file_stat) +#endif + + +static char* find_input_file(void) +{ char *fname; + int r; + if (output_directory&&!kpse_absolute_p((char*)name_of_file0, false)) + { int r=-1; + fname = concat3(output_directory, DIR_SEP_STRING, (char*)name_of_file0); + r=GET_FILE_STAT; + if (r==0) + return fname; + free(fname); + } + fname = kpse_find_tex((char*)name_of_file0); + if (fname!=NULL) + { r=GET_FILE_STAT; + if (r==0) + return fname; + free(fname); + } + fname=(char*)name_of_file0; + r=GET_FILE_STAT; + if (r==0) + return strdup(fname); + return NULL; +} + + +static void get_file_mod_date(void) +{ char *fname=NULL; + fname= find_input_file(); + time_str[0]=0; + if(fname!=NULL) + { make_time_str(file_stat.st_mtime,source_date_epoch!=NULL && force_source_date!=NULL); + free(fname);@+ + } +} + +static int get_file_size(void) +{ int s=-1; + char *fname=NULL; + fname= find_input_file(); + if(fname!=NULL) + {s=file_stat.st_size; + free(fname);@+ + } + return s; +} + + +static int get_md5_sum(int s, int file) +{ md5_state_t st; + memset(md5_digest,0,DIGEST_SIZE); + if (file) + { char *fname; + pack_file_name(s,empty_string,empty_string,NULL); + fname= find_input_file(); + if(fname!=NULL) + { FILE *f; + f = fopen(fname, "rb"); + if (f != NULL) + { int r; + char file_buf[FILE_BUF_SIZE]; + recorder_record_input(fname); + md5_init(&st); + while ((r = fread(&file_buf, 1, FILE_BUF_SIZE, f)) > 0) + md5_append(&st, (const md5_byte_t *) file_buf, r); + md5_finish(&st, md5_digest); + fclose(f); + } + free(fname); + } + else + return 0; + } + else + { md5_init(&st); + md5_append(&st,(md5_byte_t *)&str_pool[str_start[s]], + str_start[s + 1] - str_start[s]); + md5_finish(&st, md5_digest); + } + return DIGEST_SIZE; +} + +@* Profiling. +If we want to find out where and how \TeX\ spends its time, we need +a profiler. The common profilers, like {\tt gprof}, +examine the executable code of \TeX, then take some measurments at run time, +and map this information to the functions or source lines of \TeX's code. + +But \TeX\ is a programming language by itself, and when we program +in \TeX, we want to map the timing information gathered at run time +to the macros and source lines of \TeX's input files. +The profiler implemented in the follwing code serves this purpose. + +By default, profiling is initialy disabled. +To enable profiling, you can use the {\tt -prof} option on the +command line. + +@<explain the options@>= + " -prof "@/ + @t\qquad@>"\t enable profiling\n"@/ + +@ This option will set the |profile_on| variable, defined below, to true. + +@<more options@>= + { "prof", 0, &profile_on, 1 }, + +@ @<Global variables@>= +static int @!profile_on=false; + +@ To restrict profiling to selected parts of \TeX's input, +we define two primitives for switching profiling on and off. + +@d profile_on_code (eTeX_last_extension_cmd_mod+3) /* `\.{\\profileon}' */ +@d profile_off_code (eTeX_last_extension_cmd_mod+4) /* `\.{\\profileoff}' */ +@d pdftex_first_extension_code profile_off_code+1 + +@<Put each...@>= +primitive("profileon", extension, profile_on_code); +@!@:profileon\_}{\.{\\profileon} primitive@> +primitive("profileoff", extension, profile_off_code); +@!@:profileoff\_}{\.{\\profileoff} primitive@> + +@ @<Cases of |extension| for |print_cmd_chr|@>= +case profile_on_code: print_esc("profileon");@+break; +case profile_off_code: print_esc("profileoff");@+break; + +@ @<Cases for |do_extension|@>= +case profile_on_code: +if (!profile_on) +{ profile_on=true; + @<record profiling on@>@; +} +break; +case profile_off_code: +if (profile_on) +{ @<record profiling off@>@; + profile_on=false; +} +break; + +@*1 Files and Lines. +The ``instructions'' that drive the \TeX\ interpreter are called ``tokens'' +and we want to keep track of the files and the lines +where the tokens come from. +If a token comes from an external file, then \TeX's variable |line| will +contain the correct line number, and |cur_file| contains the current file. +|cur_file| is an abbreviation for |input_file[index]| with +|index| beeing a short hand for |cur_input.index_field|. +We can not use the |index| number to identify input files, +because \TeX\ will reuse these numbers, possibly many times, after a file +has been closed. Instead we make a new array |input_file_num| that +runs in parallel to |input_file| and contains a unique current file number. +Further we make a new |read_file_num| array that runs +in parallel to |read_file|. + +@d cur_file_num input_file_num[index] /*the current unique |alpha_file| number*/ + +@<Glob...@>= +static uint8_t @!input_file_num0[max_in_open], + *const @!input_file_num = @!input_file_num0-1; +static uint8_t @! read_file_num[20]; + +@ To implement the association between unique file numbers and full +file names, we use the array |file_num_name|. We will store file +numbers with 8 bits and line numbers with 16 bits in a single 32 bit +number. We do this because we will need to store this information +together with the tokens in a token list. This representation will +keep the most significant byte zero, so that we can later use it for +the ``command'' value. + +@<Global...@>= +#define FILE_NUM_BITS 8 +#define MAX_FILE_NUM ((1<<FILE_NUM_BITS)-1) +#define LINE_BITS 16 +#define MAX_LINE ((1<<LINE_BITS)-1) +#define FILE_LINE(F,L) (((F)<<(LINE_BITS))|(L)) +#define FL_FILE(FL) ((FL)>>LINE_BITS) +#define FL_LINE(FL) ((FL)&MAX_LINE) +static char * @!file_num_name[MAX_FILE_NUM+1]; +static int file_from_cmd [MAX_FILE_NUM+1]; /* first command from the file */ +static int file_to_cmd [MAX_FILE_NUM+1]; /* last command from the file */ +static int file_num; /* the the last |file_num_name| allocated */ +static uint32_t cur_file_line=0; /* the file and line for |cur_tok| */ + +@ @<check |line| for overflow@>= +if (line>=MAX_LINE) overflow("too many input lines", MAX_LINE); + +@ Whenever we have a new |full_name_of_file|, we allocate +a new unique file number. + +@<Allocate a file number@>= +if (file_num>=MAX_FILE_NUM) overflow("file number",file_num); +else file_num++; +file_num_name[file_num]=strdup(full_name_of_file); +file_from_cmd[file_num]=cmd_count; + +@ Whenever \TeX\ sets |cur_file| to a new value, we need to +set |cur_file_num| as well. + +@<Set new |cur_file_num|@>= +@<Allocate a file number@>@; +cur_file_num=file_num; + +@ Whenever \TeX\ opens |read_file[n]| it needs to set the file count. +@<Set new |read_file_num[n]|@>= +@<Allocate a file number@>@; +read_file_num[n]=file_num; + +@ A few file numbers are predefined. Some tokens, for instance those +that are loaded from a format file, have an unknown origin and are +classified as comming from an |unknown_file|. +We use |system_file| to assign time intervals to +the system and use the command field to distinguish between different +system events. There might also be tokens from the terminal, either +typed in interactively (which will messup your timing values) +or from the command line. + +For the commands from the |system_file|, we use +special line numbers to distinguish various cases. + +@d unknown_file 0 /*tokens with unknown origin, usualy from a format file*/ +@d system_file 1 /*tokens generated by the system*/ +@d terminal_file 2 /*tokens read from the terminal*/ +@d system_unknown 0 /*should not happen*/ +@d system_start 1 /*startup of \TeX*/ +@d system_end 2 /*end of \TeX*/ +@d system_ship_out 3 /*time spent in |ship_out()|*/ +@d system_line_break 4 /*time spent in |line_break()|*/ +@d system_init_trie 5 /*time spent in |line_break()|*/ +@d system_build_page 6 /*time spent in |line_break()|*/ +@d system_input_ln 7 /*time spent in |line_break()|*/ +@d system_insert 8 /*time spent on tokens inserted by \TeX*/ + + +@<Set init...@>= +file_num_name[unknown_file]="unknown"; +file_num_name[system_file]="system"; +file_num_name[terminal_file]="terminal"; +file_num=terminal_file; + +@ When \TeX\ reads a token from an external file, it has to set +|cur_file_line| based on the information in |cur_input|. +This code is parallel to the code that is used to read the next line: + +@<Set |cur_file_line| based on the information in |cur_input|@>= +if (terminal_input) + cur_file_line=FILE_LINE(terminal_file,0); +else if (name<=19) + cur_file_line=FILE_LINE(read_file_num[name-1],0); /* do I need the line? */ +else + cur_file_line=FILE_LINE(cur_file_num,line); + +@ When we end a \.{\\read} line, we set the current file and line. +@<Set |cur_file_line| when a \.{\\read} line ends@>= + cur_file_line=FILE_LINE(read_file_num[name-1],0); + + +@ As described above, we store file and line numbers as 32 bit integers. +When reading input from external files, we know the file and the line; +but for token lists we need to store this information together +with the tokens, when we create the token list. +As a simple solution, we allocate a 32 bit integer for all pointers. + +Whenever in \TeX's code a new token is generated, we know what file and +line is responsible for what is going on. The newly generated token +will then point to the same file and line. + +@<Glob...@>= +static uint32_t @!fl_mem0[mem_max-mem_min+1], + *const @!fl_mem = @!fl_mem0-mem_min; /*the big dynamic file/line storage*/ + +@*1 Timing. +When the interpreter of \TeX\ executes a command, we need to measure the +time needed to execute it and record that information in a file. +We call such a measurement a ``time stamp'' or ``stamp'' +for short. +This process should be quick, in order not to disturb the measurements +too much by the time needed for the measurement itself and its recording. +So we keep the data in a large array, and postpone writing the output file +until \TeX\ terminates. + +Now let us consider the measurement of such a time stamp. +As a general rule, the start of a new time interval is the end +of the previous time interval. There is only a single look up +of the current time for both. So all time intervals will add up +to the total run time. + +We keep the start time in two variables |start_sec| and |start_nsec|; +the first counts the seconds, the other (roughly) the nano seconds. +When we measure the end time, we should know the previous start time, +and which command was executed and from which file and line this command +was taken. The measured end time is the start time for the next command. + + +@<Global variables@>= +static long start_nsec, start_sec, diff_nsec; +struct timespec ts; +static int time_error; + +@ To get timing information, we use the POSIX function |clock_gettime|. +Depending on the operating system there are many different variations. +The timer used for timing can be selected by defining |GETTIME| at +compile time. If |GETTIME| is not set, we try to find a good default. +Currently we use |timespec_get| under Windows and |clock_gettime| +with a tread specific clock otherwise. + +@<get current time@>= +#ifdef GETTIME +#if GETTIME==0 + time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); +#elif GETTIME==1 + time_error=clock_gettime(CLOCK_PROCESS_CPUTIME_ID, &ts); +#elif GETTIME==2 + time_error=clock_gettime(CLOCK_MONOTONIC_RAW, &ts); +#elif GETTIME==3 + time_error=clock_gettime(CLOCK_MONOTONIC, &ts); +#else + time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); +#endif +#else +/* guess a default */ +#ifdef _WIN32 + time_error=timespec_get(&ts, TIME_UTC); +#else + time_error=clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); +#endif +#endif + + +@ To record the timing information, we compute the time difference in +nano seconds, assuming that it fits into unsigned 32 bit which means +the difference should be less than about 4 seconds. + +@<compute elapsed time@>= + diff_nsec= (ts.tv_sec-start_sec)*1000000000+ (ts.tv_nsec-start_nsec); +#if 0 + printf("time: %ld %ld %ld\n", diff_nsec, (ts.tv_sec), (ts.tv_nsec)); +#endif + + start_nsec=ts.tv_nsec; + start_sec=ts.tv_sec; + +@*1 Commands. +The fourth piece of information that we store with file, line, and time +is the command that \TeX\ is executing. +The elapsed time will fit into an four byte unsigned integer, and +luckily also the command, the file and the line will do as well. +When starting a new time interval, often we will not yet know the +next command, its file, and its line. +We keep the command, the file, the line, in a set of variables starting with +|prof_|. These variables my change within a time interval, and the final +values are written when the end time is known. +There are also variables related to macro calls which are explainded later +but are already defined here because we will initialize them together +with the other variables. + +@<Global variables@>= +#define MAX_STAMPS 80000000 +#define MAX_MACRO_STACK 40000 +#define POP_BIT 0x80 + +static uint32_t stamp[MAX_STAMPS]; /*one chunk of profiling information*/ +static uint32_t st_count=0; /*the next available entry in the array above*/ +static uint32_t cmd_count=0; /*the number of time stamps in the array above*/ +static int prof_cmd=0; /*the command to be written */ +static uint32_t prof_file_line=0; /*the file and line to be written */ + +@ The code below initializes the variables. Note that we use the special +command value |system_cmd|. The ``real'' command values +are those that occur in the |big_switch| ranging from 0 (|escape|) +to 100 (|max_command|), the values that are used when entering +the |main_loop|(|letter|, |other_char|, |char_given|, and |char_num|) +are a subset of these. +The pseudo command |system_cmd| is just outside of this range. +A few more pseudo commands are needed below and we define them here. + +@d system_cmd (max_command+1) /*pseudo command value*/ +@d system_profile_on (system_cmd+1) +@d system_profile_off (system_cmd+2) +@d system_macro_push (system_cmd+3) +@d system_macro_pop (system_cmd+4) +@d system_macro_pop_small (system_cmd+5) +@d system_macro_pop_0 (system_cmd+6) + + +@<Initialize profiling@>= + @<Initialize the macro call stack@>@; + prof_cmd=system_cmd; + prof_file_line=FILE_LINE(system_file,system_start); + @<record macro call information if necessary@>@; +#if 0 + clock_getres(CLOCK_MONOTONIC, &ts); + printf("CLOCK_MONOTONIC: %ld %ld\n",ts.tv_sec,ts.tv_nsec); + + clock_getres(CLOCK_MONOTONIC_RAW, &ts); + printf("CLOCK_MONOTONIC_RAW: %ld %ld\n",ts.tv_sec,ts.tv_nsec); + + clock_getres(CLOCK_PROCESS_CPUTIME_ID, &ts); + printf("CLOCK_PROCESS_CPUTIME_ID: %ld %ld\n",ts.tv_sec,ts.tv_nsec); + + clock_getres(CLOCK_THREAD_CPUTIME_ID, &ts); + printf("CLOCK_THREAD_CPUTIME_ID: %ld %ld\n",ts.tv_sec,ts.tv_nsec); +#endif + @<get current time@>@; + diff_nsec=0; + start_nsec=ts.tv_nsec; + start_sec=ts.tv_sec; + +@ At the end of \TeX\ we check |last_depth| and clean the remaining +macro stack if necessary. We end the record always with recording +timing information. + +@<pop the macro call stack at the end of \TeX@>= +prof_cmd=cur_cmd; /* the stop command */ +prof_file_line=cur_file_line; +prof_depth=0; +profile_on=true; +@<get current time@>@; /* advance the time */ +@<compute elapsed time@>@; +@<record timing information@>@; /* pop to |prof_depth| */ + + +@ When we record the timing information, +we first store command, file, and line, and then store +the elapsed time. + +@d SHOW_RECORD_TIMING 0 + +@<record timing information@>= +if (profile_on) +{ if (st_count+3>MAX_STAMPS) + overflow("profile data",MAX_STAMPS); + @<record poping the macro stack if necessary@>@; + stamp[st_count++]= (prof_cmd<<24) | prof_file_line; + prof_cmd=prof_cmd&~POP_BIT; /*remove a possible |POP_BIT|*/ +#if SHOW_RECORD_TIMING + print_char('<');print_int(cmd_count); + print_char(':'); + if (prof_cmd&POP_BIT) print_char('-'); + print_int(prof_cmd&~POP_BIT); + print_char(':');print_int(prof_depth); + print_char('>');print_ln(); +#endif + cmd_count++; + @<get current time@>@; + @<compute elapsed time@>@; + stamp[st_count++]=diff_nsec; +} + + +@ To mark the execution of starting and ending profiling +we use the system commands for the start and the end of profiling. + +After switching profiling off, no more data is written to the output +file. So we have to record the macro call and timing information +explicitly before switching the profiling off. + +@<record profiling off@>= + prof_file_line=cur_file_line; + prof_cmd=system_profile_off; + prof_depth=cur_depth; + @<record macro call information if necessary@>@; + @<record timing information@>@; + +@ When switching profiling on, we have to syncronize the start time +with the current time and prepare everything for the recording +of the timing before entering the main loop again. +Setting the command, file and line information is simple. +Since we can not assume that the macro call stack is unchanged, +we record the necessary changes. + +@<record profiling on@>= + @<get current time@>@; + diff_nsec=0; + start_nsec=ts.tv_nsec; + start_sec=ts.tv_sec; + prof_file_line=cur_file_line; + prof_cmd=system_profile_on; + prof_depth=cur_depth; + @<record macro call information if necessary@>@; + +@ At the end of \TeX\ we record the timing for the |stop| command +and pop the macro call stack. + +@<record the end of \TeX@>= +@<pop the macro call stack at the end of \TeX@>@; + +@ There are two places where we regularly record the next command. +The first one is when \TeX\ enters the big switch. Here +we record the timing information before reading the next expanded token +and set the current file, line, and command after reading the +next expanded token. +The other place is at the start of the main loop. Here both +actions are done one after the other. Whenever we assiciate +a command with the current time slot by setting |prof_cmd| and +the profile data associated with it, we need to make sure that +changes of entries in the macro call are reported to the profiler. + +@<set current file, line, and command for the current time slot@>= +if (profile_on) +{ prof_cmd=cur_cmd; + prof_depth=cur_depth; + prof_file_line=cur_file_line; + @<record macro call information if necessary@>@; +} + +@ Some time expensive routines, |line_break| and |ship_out|, consume +time that depends entirely on the output generated by \TeX\ but +not on how that output is generated. So we charge the time spent in these +routines on the system. + +@<Local variables to save the profiling context@>= +int tmp_cmd; +int tmp_file_line; +int tmp_depth; + +@ @<Charge the time used here on |line_break|@>= +if (profile_on) +{ @<record timing information@>@; + tmp_file_line=prof_file_line; + tmp_cmd=prof_cmd; + tmp_depth=prof_depth; + prof_file_line=FILE_LINE(system_file,system_line_break); + prof_cmd=system_cmd; +} + + +@ @<restore the previous current file, line, and command@>= +if (profile_on) +{ @<record timing information@>@; + prof_file_line=tmp_file_line; + prof_cmd=tmp_cmd; + prof_depth=tmp_depth; +} + + + +@ @<Charge the time used here on |init_trie|@>= +if (profile_on) +{ @<record timing information@>@; + tmp_file_line=prof_file_line; + tmp_cmd=prof_cmd; + tmp_depth=prof_depth; + prof_file_line=FILE_LINE(system_file,system_init_trie); + prof_cmd=system_cmd; +} + + +@ @<Charge the time used here on |build_page|@>= +if (profile_on) +{ @<record timing information@>@; + tmp_file_line=prof_file_line; + tmp_cmd=prof_cmd; + tmp_depth=prof_depth; + prof_file_line=FILE_LINE(system_file,system_build_page); + prof_cmd=system_cmd; +} + + + +@ @<Charge the time used here on |ship_out|@>= +if (profile_on) +{ @<record timing information@>@; + tmp_file_line=prof_file_line; + tmp_cmd=prof_cmd; + tmp_depth=prof_depth; + prof_file_line=FILE_LINE(system_file,system_ship_out); + prof_cmd=system_cmd; +} + + +@*1 Macro Calls. +Let's talk about collecting information about macro calls. +Macro calls come in various flavours: +There are active characters and single letter control sequences; +both have their own region in the |eqtb| table of equivalents. +Then there is an empty control sequence, and last there +are lots of (currently up to 45000) multiletter control sequences. +The plain format defines about 1000 and the \LaTeX\ format about 23000 +of them. When |get_next| finds a control sequence, it sets |cur_cs| +to the index in the table of equivalents. +It will further set |cur_cmd=eq_type(cur_cs)|, |cur_chr=equiv(cur_cs)|, +and |cur_tok=cs_token_flag+cur_cs|. + +The |get_x_token| or |expand| procedure will then notice the macro call +and invoke |macro_call|. +|macro_call| will inspect the call site and the parameter section +of the macro and put the actual macro parameters on the parameter +stack before pushing the macro body on the input stack. + +But wait! |macro_call| will apply a technique called last call optimization +before doing any of the above: It will remove all entries from +the input stack that have no more tokens left for processing. +This allows tail recursive macro calls in constant stack space and +makes long recursive loops possible. +For example building the \LaTeX\ format uses a loop to read the +definition of UTF8 codes. +This loop has more than 35000 iterations and a nesting depth +of the same size. With last call optimization, the loop uses less than 3000 +entries on the input stack. + +Popping the stack early makes it slightly more difficult +to track who is calling whom---an information that we need if we want +to give cumulative timing for a macro call. Cumulative timing is the time spent +during that macro call including all the calls to sub-macros. + +The timing information that we collected in the previous section +assigned the time caused by the execution of \TeX's commands to the +specific line and file that caused the execution of that command. +As a consequnce, the time used for the expansion of a macro call +is assigned to the file and line that contains the call site of the macro. +The time that is spent on executing the commands in the macros body +are assigned to the file and line containing the macro definition. +But there is no information on the call graph that allows us to +classify one command as comming from a lower level of expansion than +the other. + +In this section, we want to assign the time used for the execution +of commands to a macro definition, with its name and the file and line +that contains the definition of the macro. +While the name of a macro is an important information, it is not +sufficient to relate the time to the macros name, because +the same name can be used for several different macros at various times. + +So we collect for a macro call the following data: + +\yskip\textindent{1)}The index |cur_cs| into the table of equivalents +|eqtb|. Using |cur_cs| it is possible to access the macros name. For +active characters and single letter control sequences, we obtain the +character by subtracting |active_base| or |single_base| from |cur_cs|. +For multi-letter control sequences, we find the index to |str_start| +for the macro name in |text(cur_cs)|, the text field of the corresponding +hash table entry. The association between |cur_cs| and the macro name +is stored in a separate section of the profilers output file. + +\yskip\textindent{2)}The line and file of the macro definition. +We store this information in the |fl_mem| array using |equiv(cur_cs)|, +the pointer to the reference count token of the macro. + +\yskip\textindent{3)}The ``unoptimized'' nesting depth of the macro call. + +We store this information in a 64 bit integer and define macros +to assemble and disassemble this integer. +From this information it is possible to reconstruct the call graph +and compute cumulative timing information for macro calls. +We keep the information for the whole ``unoptimized'' macro stack +in the |macro_stack|. +The varable |prof_depth| tells us the nesting depth of |prof_cmd|. +The variable |last_depth| tells us the nesting depth +of the last recorded time stamp and the variable |unchanged_depth| +tells us which part of the macro stack is unchanged since then. + + +@d CALL_DEPTH(A) ((A)>>48) +@d CALL_EQTB(A) (((A)>>32)&0xFFFF) +@d CALL_FILE(A) (((A)>>16)&0xFF) +@d CALL_LINE(A) ((A)&0xFFFF) +@d CALL_DE(A) ((A)>>32) +@d CALL_CFL(A) ((A)&0xFFFFFFFF) +@d CALL_INFO(D,C,FL) (((uint64_t)(D)<<48)|((uint64_t)(C)<<32)| + (system_macro_push<<24)|FL) + +@<Global variables@>= +static uint64_t macro_stack[MAX_MACRO_STACK]={0}; /* the macro calls */ +static int prof_depth; /* the nesting depth of macros */ +static int last_depth; /* the nesting depth last report1ed*/ +static int unchanged_depth; /* up to here no change on |macro_stack|*/ +static int prof_max_depth; + +@ @<Initialize the macro call stack@>= +{ int macro_depth=-1, macro_cs=0; + uint32_t macro_fl=FILE_LINE(terminal_file,0); + @<update the macro stack@>@; + cmd_count++; + stamp[st_count++]=CALL_CFL(macro_stack[0]); + stamp[st_count++]=CALL_DE(macro_stack[0]); + last_depth=unchanged_depth=prof_depth=0; +} + +@ We maintain the macro stack in the |macro_call| procedure +by adding local variables that capture the information of +the call token that cause the invocation of |macro_call|. + +@<additional local variables for |macro_call|@>= +uint32_t macro_fl=fl_mem[cur_chr]; +int macro_cs=cur_cs; +int macro_depth=cur_depth; + +@ At the end of |macro_call|, we use this information to +update the |macro_stack| and related variables. + +@<update the macro stack@>= +macro_depth++; +cur_depth=macro_depth; +if (macro_depth>=MAX_MACRO_STACK) + overflow("macro stack size", macro_depth); +macro_stack[macro_depth]=CALL_INFO(macro_depth,macro_cs,macro_fl); +if (macro_depth<=unchanged_depth) + unchanged_depth=macro_depth-1; + +@ We add files as part of the macro stack using the following code: +@<additional local variables for |start_input|@>= +uint32_t macro_fl=FILE_LINE(cur_file_num,0); +int macro_cs=0; +int macro_depth=cur_depth; + +@ We add calls to the output routine and hook macros like \.{\\everypar}, \.{\\everymath}, etc. to the macro stack using tis code: +@<additional local variables for |begin_token_list|@>= +uint32_t macro_fl=fl_mem[p]; +int macro_cs=undefined_control_sequence+t; +/*|undefined_control_sequence| is the last pointer in the hash table*/ +int macro_depth=cur_depth; + + +@ As we have seen above, recording timing information consists +of two steps: +In the first step we set global variables like |prof_cmd| and |prof_depth| +based on the current command. Then we wait until the command is executed, +and in the second step we compute the elapsed time and store the +information from the first step together with the time in the |stamp| +array. +Now we augment this process and store macro call related information. + +The key value that guides this process is |prof_depth|: +the macro nesting depth associated with |prof_cmd|. +Using it we can get from the |macro_stack| information about +all currently active macros at lower nesting levels. +Two more variables |last_depth| and |unchanged_depth| are related to it: +After recording macro call information for a command, +we set |last_depth=unchanged_depth=prof_depth|. +The next time we need to record macro call information, +|unchanged_depth| and |prof_depth| may have changed, but +|last_depth| should be unchanged. Because |unchanged_depth| +can only decrease, we know that $|unchanged_depth|\le|last_depth|$. +On the other hand, |prof_depth| can increase as well as decrease. +This gives us the following cases: + +\yskip\textindent{$|unchanged_depth|<|prof_depth|$} +Record the new stack entries from $|unchanged_depth|+1$ up to |prof_depth|. + +\yskip\textindent{$|prof_depth|=|unchanged_depth|=|last_depth|$:} +No information on macro calls is necessary. + +\yskip\textindent{$|prof_depth|<|unchanged_depth|=|last_depth|$:} +Record that the stack was popped from |last_depth| down to |prof_depth|. + +\yskip\textindent{$|prof_depth|\le|unchanged_depth|<|last_depth|$:} +Record that the stack was popped from |last_depth| down to |prof_depth|. + + +The common case that $|prof_dept|=|last_dept|-1$ asks for a more +compact representation and we use the most significant bit of the +command value to encode this case. +Note that we count this as a separate command. + +It would be nice if we could wait with recording the stack changes until +the command is completely executed. The command however might read +parameters which causes pops and pushes on the macro stack. So by the +time the command has finished, the stack might have changed considerably. +There is a further complication: the execusion of some commands does +not end with a jump to |big_switch| but with a jump to |big_reswitch|. +This will set a new value for |prof_cmd| and so will invalidate all our +preparations. +For this reason we need to make the recording of macro call information +idempotent. Recording several pops and pushes explicitly does not harm +(except for extra output) but setting the pop bit asks for trouble. +Therefore we postpone the recording of poping the macro stack. + + +@<record macro call information if necessary@>= +if (unchanged_depth<prof_depth) + @<record the new stack entries@>@; + + +@ @<record poping the macro stack if necessary@>= +if (last_depth>prof_depth) +{ if (last_depth==prof_depth+1) + prof_cmd=prof_cmd|POP_BIT; + else + stamp[st_count++]=(system_macro_pop<<24)| (last_depth-prof_depth); +#if SHOW_RECORD_TIMING + print_char('{');print_int(cmd_count); + print_char(':');print_int(system_macro_pop); + print_char(':');print_int(last_depth); + print_char('>');print_int(prof_depth); + print_char('}');print_ln(); +#endif + cmd_count++; + last_depth=unchanged_depth=prof_depth; +} + + +@ The information about new stack entries are taken from the |macro_stack|. + +@<record the new stack entries@>= +{ int i; + if (last_depth>unchanged_depth) + { +#if SHOW_RECORD_TIMING + print_char('{');print_int(cmd_count); + print_char(':');print_int(system_macro_pop); + print_char(':');print_int(last_depth); + print_char('>');print_int(unchanged_depth); + print_char('}');print_ln(); +#endif + cmd_count++; + } + if (st_count+2*(prof_depth-unchanged_depth)>MAX_STAMPS) + overflow("profile data",MAX_STAMPS); + for (i=unchanged_depth+1;i<=prof_depth;i++) + { +#if SHOW_RECORD_TIMING + print_char('[');print_int(cmd_count); + print_char(':');print_int(i-1); + print_char('<');print_int(CALL_DEPTH(macro_stack[i])); + print_char(':'); print_cs(CALL_EQTB(macro_stack[i])); + print_char(']');print_ln(); +#endif + cmd_count++; + stamp[st_count++]=CALL_CFL(macro_stack[i]); + stamp[st_count++]=CALL_DE(macro_stack[i]); + } + last_depth=unchanged_depth=prof_depth; + if (prof_depth>prof_max_depth) prof_max_depth=prof_depth; +} + + +@*1 Output the profile data. +Here is the code that outputs the results. +The format tries to be simple but flexible. +The output file has four parts: size data, file names, macro names, +and time stamps. To enable some error checking, the three file +parts start and end with an output marker. + +% we have typically from 1000 to 5000, in exteme cases up to 30000, +% file/line entries +% cut -d : -f 2,3 tmon.out | fgrep -v '.' | sort |uniq | wc -l + +@<Finish the extensions@>= +{ FILE *prof; + pack_job_name(".tprof"); + prof=fopen((char*)name_of_file0,"wb"); + if (prof==NULL) print_err("Unable to write the profile data\n"); + else { + int i; + prof_file_line=cur_file_line; + @<output marker@>@; + @<output size data@>@; + @<output marker@>@; + @<output file names@>@; + @<output marker@>@; + @<output macro names@>@; + @<output marker@>@; + @<output timing data@>@> + @<output marker@>@; + fclose(prof); + } +} + +@ The output marker consists of eight byte: the ASCII codes of ``TEX PROF''. + +@<output marker@>= + fputs("TEX PROF",prof); + + +@ The information necessary to process the file comes after the first +marker. Here and in the following all numbers are written out in +``big-endian byte order''. We provide the following information: + +The number of files (2 byte), +the number of byte used for filenames +including the zero bytes (2 byte), +the number of time stamps (4 byte). +The number of macro names (2 byte), +the number of bytes used for macro names (4 byte), +again including the zero bytes, +and the maximum nesting depth used on the |macro_stack|. + +We start with the macros to write multibyte integers in bigendian order. + +@d PUT1(N) fputc((N)&0xFF,prof) +@d PUT2(N) PUT1((N)>>8),PUT1(N) +@d PUT4(N) PUT2((N)>>16),PUT2(N) + + +@<output size data@>= + PUT2(file_num+1); /* the number of files */ +{ int n, m; + m=0; + for (i=0; i<=file_num; i++) + { n=strlen(file_num_name[i]); + m+=n+1; + } + PUT2(m); /* the number of byte used for file names */ +} + PUT4(cmd_count); /* the number of commands */ +{ int k, m; + m=k=0; + for (i=hash_base; i<undefined_control_sequence; i++) + if (text(i)!=0) + { k++; + m+=(str_start[text(i)+1]-str_start[text(i)])+1; + } + PUT2(k); /* the number of macro names */ + PUT4(m); /* the number of byte for the macro names */ + PUT2(prof_max_depth); /*the maximum macro nesting depth*/ +} + + +@ Writing the file names is simple. + +@<output file names@>= +for (i=0; i<=file_num; i++) + { fputs(file_num_name[i], prof); + PUT1(0); + } + +@ Here is the code to write the macro names to the output file. +It traverses the |hash_table| from |hash_base| up to +|undefined_control_sequence|. Each macro name is preceeded by +its index in the hash table stored in two byte. + +@<output macro names@>= +for (i=hash_base; i<undefined_control_sequence; i++) + if (text(i)!=0) + { int k; + PUT2(i); + for (k=str_start[text(i)];k<str_start[text(i)+1];k++) + PUT1(str_pool[k]); + PUT1(0); + } + + +@ Next we output the time stamps. We use a variable format +depending on the command value which is always in the top byte +of the stamp. We optimize the output size slightly: +For macro calls the |stamp| array contains the depth values. +It is of course more efficient to write the relative changes +into the output instead of the absolute values. +A most common case is poping the stack by one. This is recorded +in the |stamp| array by setting the |POP_BIT| in the command value +and we simply keep this bit. Small relative values $n$ in the range +$0 \le n \le 10$ are encoded using the command value +$|system_macro_pop_0|+n$. Values in the range $11 \le n \le 255$ +encoded as single byte values following the commmand code +|system_macro_pop_small|. All other values are encoded using +two byte following the command code |system_macro_pop|. +Encoding the pops separate from the calls, we need no longer +store the depth values with the calls. + +@<encode pop $n$@>= +#if SHOW_WRITE_TIMING + printf("!%d:%d:%d>%d\n",j++,i,last_depth,last_depth-n); +#endif + if (n<=10) + PUT1(system_macro_pop_0+n); + else if (n<=255) + { PUT1(system_macro_pop_small); + PUT1(n); + } + else + { PUT1(system_macro_pop); + PUT2(n); + } + last_depth=last_depth-n; + +@ Now we can output the timing data. + +@d SHOW_WRITE_TIMING 0 + +@<output timing data@>= + + i=0; +{ int j=0; + last_depth=-1; + while (i< st_count) + { int8_t c = stamp[i]>>24; + if (c==system_macro_pop) + { int n=stamp[i]&0xFFFF; + @<encode pop $n$@>@; + i++; + } + else if (c==system_macro_push) + { int d=stamp[i+1]>>16; + if (d<=last_depth) + { int n=last_depth-d+1; + @<encode pop $n$@>@; + /* here I could optimize the case $n=1$ by setting the |POP_BIT| + in |system_macro_push|*/ + } +#if SHOW_WRITE_TIMING + print_char('!');print_int(j++); + print_char(':');print_int(i); + print_char(':'); print_int(system_macro_push); + print_char(':');print_int(d); + print_char(':'); print_cs(stamp[i+1]&0xffff); + print_char(']');print_ln(); +#endif + PUT4(stamp[i]); i++; + PUT2(stamp[i]); i++; + last_depth=d; + } + else + { + if (c&POP_BIT) + { last_depth--; +#if SHOW_WRITE_TIMING + printf("!%d:%d:%d>%d}\n",j++,i,last_depth+1,last_depth); +#endif + } +#if SHOW_WRITE_TIMING + printf("!%d:%d:%d:%d>\n",j++,i,stamp[i]>>24,last_depth); +#endif + PUT4(stamp[i]);i++; + PUT4(stamp[i]);i++; + } + } +} + +@*1 Dumping the profiling information. + +We dump the number of files first and then dump +the file names including the +terminating zero byte. +We put eight byte into a memory word and dump them together. +The last memory word is padded with zeros. + +@<Dump the full file names@>= +{ two_halves hw; + int i,j; + hw.rh=file_num; + i=0; + for (k=0; k<=file_num; k++) + { j=strlen(file_num_name[k]); + if (j>i) i=j; + } + hw.lh=i; + dump_hh(hw); +} +{ memory_word m; + int word_count=0; + int byte_count=0; + m.w=0; + for (k=0;k<=file_num;k++) + { char *s=file_num_name[k]; + do + { m.w=(m.w<<8)|*s; + byte_count++; + if (byte_count==8) + { dump_wd(m); word_count++; + m.w=0; byte_count=0; + } + } while (*s++!=0); + } + if (byte_count >0) + { while (byte_count<8) + { m.w=(m.w<<8)|0; + byte_count++; + } + dump_wd(m);word_count++; + } + print_ln();print_int(file_num); + print(" file names dumped; total size "); print_int(word_count*8); +} + +@ @<Undump the full file names@>= + +{ two_halves hw; + memory_word m; + int byte_count=0; + int i,j; + char *buf; + undump_hh(hw); + file_num=hw.rh; + if (file_num>MAX_FILE_NUM) overflow("file number",file_num); + i=hw.lh; + buf=malloc(i+1); + if (buf==NULL) + { print_err("Out of memory while reading filenames from format file"); + succumb; + } + for (k=0;k<=file_num;k++) + { char *s=buf; + do + { if (byte_count==0) + { undump_wd(m); byte_count=8; } + *s=(m.w>>56)&0xFF; m.w=m.w<<8; + byte_count--; + } while (*s++!=0); + file_num_name[k]=strdup(buf); + if (file_num_name[k]==NULL) + { print_err("Out of memory while reading filenames from format file"); + succumb; + } + } + free(buf); +} + + +@ +Next we dump the file and line information from the |fl_mem| array. +Because some formats leave many unallocated memory words in the |avail| +list we dump only those memory words that are not on the avail list. +We set the entry |fl_mem[p]| to zero if |p| is on the |avail| list +while we traverse the list when dumping the memory words. +Besides the unallocated entries also entries that specify the unknown +file with line zero are completely zero. We do not dump these as well. + +When dumping an entry we set the topmost bit of the entry to 1. +An entry that has the topmost bit equal to zero contains the count +of the following zero entries. We dump entries in pairs since +the dump file is organized as a file of memory words + +@<Dump the file and line information@>= +{ int word_count=0; + k=hi_mem_min; + while (k<=mem_end) + { two_halves hw; + halfword e; + int word_count=0; + @<compute the next file line entry@>@; + hw.rh=e; + if (k>mem_end) e=0; + else + { @<compute the next file line entry@>@; } + hw.lh=e; + dump_hh(hw); word_count++; + } + print_ln(); print_int(word_count); + print(" words of file/line information dumped."); +} +@ @<compute the next file line entry@>= + if (fl_mem[k]!=0) e=fl_mem[k++]|0x80000000; + else + { int i; + i=1; k++; + while (k<=mem_end && fl_mem[k]==0) + { i++; k++; } + e=i; + } + +@ @<Undump the file and line information@>= +k=hi_mem_min; +while (k<=mem_end) +{ two_halves hw; + halfword e; + undump_hh(hw); + e=hw.rh; + @<get the next file line entry@>@; + if (k<=mem_end) + { e=hw.lh; + @<get the next file line entry@>@; + } +} + +@ @<get the next file line entry@>= +if (e&0x80000000) + fl_mem[k++]=e&~0x80000000; +else +{ int i; + i=e; + while (i>0) + {i--; k++;} +} + +@*1 Profiling \.{PDF} Specific Macros. +Macro packages providing \.{PDF} specific features typicaly test +whether the current engine is generating \.{PDF} output +before activating \.{PDF}-specific macros, and these macros +then use \.{PDF}-specific primitives. To be able to profile +these macros, the engine must provide (or at least pretend to provide) +the necessary primitives to make the test for \.{PDF} output succeed +and prevent the macros from failing because of undefined +primitives or unexpected behaviour. Generating \.{PDF} ouput +is usualy not required. + +The {\tt -pdf} option is designed to provide the necessary behaviour +by implementing replacements for the primitives of \pdfTeX. + +@<explain the options@>= + " -pdf "@/ + @t\qquad@>"\t pretend to be pdftex; requires -ini; implies -ltx\n"@/ + +@ This option will set the |pdf_on| variable, defined below, to true. + The option requires the {\tt -ini} option because its effect is just + the definition of additional primitives during initialization. It further + implies the {\tt -ltx} option because the primitives necesary to + run \LaTeX\ are part of \pdfTeX. + +@<handle the options@>= +else @+if (ARGUMENT_IS ("pdf")) @t\2@> + if (iniversion) pdf_on=ltxp=true; + else + { fprintf(stderr,"-pdf requires -ini; try -ini -pdf\n"); + exit(1); + } + +@ @<more options@>= + { "pdf", 0, 0, 0 }, + +@ @<Global variables@>= +static int @!pdf_on=false; + +@ Let us start with implementing the \.{PDF} version primitive. +This primitive uses the |last_item| command with a special modifier +\pdfTeX\ adds the codes for its extensions: |pdftex_version_code|, \dots +between \TeX's |badness_code| and \eTeX's |eTeX_int| codes. +Some codes like |elapsed_time_code| or |random_seed_code| are already defined above +but we duplicate them here for simplicity. +So here is the pdf version as well as all of PDF\TeX's command codes +for the |last_item| command: + +@d pdftex_version 140 /* \.{\\pdftexversion} */ +@d pdftex_revision "25" /* \.{\\pdftexrevision} */ +@d pdftex_version_string "-1.40.25" /*current \pdfTeX\ version*/ +@# +@d pdftex_first_rint_code (badness_code+1) /*base for \pdfTeX's command codes*/ +@d pdftex_version_code (pdftex_first_rint_code+0) /*code for \.{\\pdftexversion}*/ +@d pdf_last_obj_code (pdftex_first_rint_code+1) /*code for \.{\\pdflastobj}*/ +@d pdf_last_xform_code (pdftex_first_rint_code+2) /*code for \.{\\pdflastxform}*/ +@d pdf_last_ximage_code (pdftex_first_rint_code+3) /*code for \.{\\pdflastximage}*/ +@d pdf_last_ximage_pages_code (pdftex_first_rint_code+4) /*code for \.{\\pdflastximagepages}*/ +@d pdf_last_annot_code (pdftex_first_rint_code+5) /*code for \.{\\pdflastannot}*/ +@d pdf_retval_code (pdftex_first_rint_code+6) /*global multi-purpose return value*/ +@d pdf_last_ximage_colordepth_code (pdftex_first_rint_code+7) /*code for \.{\\pdflastximagecolordepth}*/ +@d pdf_last_link_code (pdftex_first_rint_code+8) /*code for \.{\\pdflastlink}*/ +@d pdftex_last_item_codes (pdftex_first_rint_code+8) /*end of \pdfTeX's command codes*/ + +@ Using these codes we can add the primitives: + +@<Put each...@>= +if (pdf_on) +{ +primitive("pdftexversion", last_item, pdftex_version_code);@/ +@!@:pdftex\_version\_}{\.{\\pdftexversion} primitive@> +primitive("pdflastobj", last_item, pdf_last_obj_code);@/ +@!@:pdf\_last\_obj\_}{\.{\\pdflastobj} primitive@> +primitive("pdflastxform", last_item, pdf_last_xform_code);@/ +@!@:pdf\_last\_xform\_}{\.{\\pdflastxform} primitive@> +primitive("pdflastximage", last_item, pdf_last_ximage_code);@/ +@!@:pdf\_last\_ximage\_}{\.{\\pdflastximage} primitive@> +primitive("pdflastximagepages", last_item, pdf_last_ximage_pages_code);@/ +@!@:pdf\_last\_ximage\_pages\_}{\.{\\pdflastximagepages} primitive@> +primitive("pdflastannot", last_item, pdf_last_annot_code);@/ +@!@:pdf\_last\_annot\_}{\.{\\pdflastannot} primitive@> +primitive("pdfretval", last_item, pdf_retval_code);@/ +@!@:pdf\_retval\_}{\.{\\pdfretval} primitive@> +primitive("pdflastximagecolordepth", last_item, pdf_last_ximage_colordepth_code);@/ +@!@:pdf\_last\_ximage\_colordepth\_}{\.{\\pdflastximagecolordepth} primitive@> +primitive("pdflastlink", last_item, pdf_last_link_code);@/ +@!@:pdf\_last\_link\_}{\.{\\pdflastlink} primitive@> +} + +@ Next we need to print the commands: + +@<Cases of |last_item| for |print_cmd_chr|@>= + case pdftex_version_code: print_esc("pdftexversion");@+break; + case pdf_last_obj_code: print_esc("pdflastobj");@+break; + case pdf_last_xform_code: print_esc("pdflastxform");@+break; + case pdf_last_ximage_code: print_esc("pdflastximage");@+break; + case pdf_last_ximage_pages_code: print_esc("pdflastximagepages");@+break; + case pdf_last_annot_code: print_esc("pdflastannot");@+break; + case pdf_retval_code: print_esc("pdfretval");@+break; + case pdf_last_ximage_colordepth_code: print_esc("pdflastximagecolordepth");@+break; + case pdf_last_link_code: print_esc("pdflastlink");@+break; + +@ The implementation needs a few global variables. + +@<Glob...@>= +static int +@!pdf_last_obj, +@!pdf_last_xform, +@!pdf_last_ximage, +@!pdf_last_ximage_pages, +@!pdf_last_annot, +@!pdf_retval, +@!pdf_last_ximage_colordepth, +@!pdf_last_link; + + +@ And this is sufficient to finish the implementation of this first batch of primitives: +@<Cases for fetching an integer value@>= + case pdftex_version_code: cur_val=pdftex_version;@+break; + case pdf_last_obj_code: cur_val=pdf_last_obj;@+break; + case pdf_last_xform_code: cur_val=pdf_last_xform;@+break; + case pdf_last_ximage_code: cur_val=pdf_last_ximage;@+break; + case pdf_last_ximage_pages_code: cur_val=pdf_last_ximage_pages;@+break; + case pdf_last_annot_code: cur_val=pdf_last_annot;@+break; + case pdf_retval_code: cur_val=pdf_retval;@+break; + case pdf_last_ximage_colordepth_code: cur_val=pdf_last_ximage_colordepth;@+break; + case pdf_last_link_code: cur_val=pdf_last_link;@+break; + + +@ The next group of primitves uses the assignment commands. We start +by defining the necessary command codes followed by the primitives. + +@d pdftex_first_loc tex_toks /*base for \pdfTeX's token list parameters*/ +@d pdf_pages_attr_loc (pdftex_first_loc+0) /*points to token list for \.{\\pdfpagesattr}*/ +@d pdf_page_attr_loc (pdftex_first_loc+1) /*points to token list for \.{\\pdfpageattr}*/ +@d pdf_page_resources_loc (pdftex_first_loc+2) /*points to token list for \.{\\pdfpageresources}*/ +@d pdf_pk_mode_loc (pdftex_first_loc+3) /*points to token list for \.{\\pdfpkmode}*/ +@d pdf_toks (pdftex_first_loc+4) /*end of \pdfTeX's token list parameters*/ +@# +@d pdftex_first_integer_code tex_int_pars /*base for \pdfTeX's integer parameters*/ +@d pdf_output_code (pdftex_first_integer_code+0) /*switch on PDF output if positive*/ +@d pdf_compress_level_code (pdftex_first_integer_code+1) /*compress level of streams*/ +@d pdf_decimal_digits_code (pdftex_first_integer_code+2) /*digits after the decimal point of numbers*/ +@d pdf_move_chars_code (pdftex_first_integer_code+3) /*move chars 0..31 to higher area if possible*/ +@d pdf_image_resolution_code (pdftex_first_integer_code+4) /*default image resolution*/ +@d pdf_pk_resolution_code (pdftex_first_integer_code+5) /*default resolution of PK font*/ +@d pdf_unique_resname_code (pdftex_first_integer_code+6) /*generate unique names for resouces*/ +@d pdf_option_always_use_pdfpagebox_code (pdftex_first_integer_code+7) /*if the PDF inclusion should always use a specific PDF page box*/ +@d pdf_option_pdf_inclusion_errorlevel_code (pdftex_first_integer_code+8) /*if the PDF inclusion should treat pdfs newer than |pdf_minor_version| as an error*/ +@d pdf_major_version_code (pdftex_first_integer_code+9) /*integer part of the PDF version produced*/ +@d pdf_minor_version_code (pdftex_first_integer_code+10) /*fractional part of the PDF version produced*/ +@d pdf_force_pagebox_code (pdftex_first_integer_code+11) /*if the PDF inclusion should always use a specific PDF page box*/ +@d pdf_pagebox_code (pdftex_first_integer_code+12) /*default pagebox to use for PDF inclusion*/ +@d pdf_inclusion_errorlevel_code (pdftex_first_integer_code+13) /*if the PDF inclusion should treat pdfs newer than |pdf_minor_version| as an error*/ +@d pdf_gamma_code (pdftex_first_integer_code+14) +@d pdf_image_gamma_code (pdftex_first_integer_code+15) +@d pdf_image_hicolor_code (pdftex_first_integer_code+16) +@d pdf_image_apply_gamma_code (pdftex_first_integer_code+17) +@d pdf_adjust_spacing_code (pdftex_first_integer_code+18) /*level of spacing adjusting*/ +@d pdf_protrude_chars_code (pdftex_first_integer_code+19) /*protrude chars at left/right edge of paragraphs*/ +@d pdf_tracing_fonts_code (pdftex_first_integer_code+20) /*level of font detail in log*/ +@d pdf_objcompresslevel_code (pdftex_first_integer_code+21) /*activate object streams*/ +@d pdf_adjust_interword_glue_code (pdftex_first_integer_code+22) /*adjust interword glue?*/ +@d pdf_prepend_kern_code (pdftex_first_integer_code+23) /*prepend kern before certain characters?*/ +@d pdf_append_kern_code (pdftex_first_integer_code+24) /*append kern before certain characters?*/ +@d pdf_gen_tounicode_code (pdftex_first_integer_code+25) /*generate ToUnicode for fonts?*/ +@d pdf_draftmode_code (pdftex_first_integer_code+26) /*switch on draftmode if positive*/ +@d pdf_inclusion_copy_font_code (pdftex_first_integer_code+27) /*generate ToUnicode for fonts?*/ +@d pdf_suppress_warning_dup_dest_code (pdftex_first_integer_code+28) /*suppress warning about duplicated destinations*/ +@d pdf_suppress_warning_dup_map_code (pdftex_first_integer_code+29) /*suppress warning about duplicated map lines*/ +@d pdf_suppress_warning_page_group_code (pdftex_first_integer_code+30) /*suppress warning about multiple pdfs with page group*/ +@d pdf_info_omit_date_code (pdftex_first_integer_code+31) /*omit generating CreationDate and ModDate*/ +@d pdf_suppress_ptex_info_code (pdftex_first_integer_code+32) /*suppress /PTEX.* entries in PDF dictionaries*/ +@d pdf_omit_charset_code (pdftex_first_integer_code+33) /*suppress /PTEX.* entries in PDF dictionaries*/ +@d pdf_omit_info_dict_code (pdftex_first_integer_code+34) /*suppress /PTEX.* entries in PDF dictionaries*/ +@d pdf_omit_procset_code (pdftex_first_integer_code+35) /*suppress /PTEX.* entries in PDF dictionaries*/ +@d pdf_int_pars (pdftex_first_integer_code+36) /*total number of \pdfTeX's integer parameters*/ +@# +@d pdftex_first_dimen_code 23 /*first number defined in this section*/ +@d pdf_h_origin_code (pdftex_first_dimen_code+0) /*horigin of the PDF output*/ +@d pdf_v_origin_code (pdftex_first_dimen_code+1) /*vorigin of the PDF output*/ +@d pdf_link_margin_code (pdftex_first_dimen_code+2) /*link margin in the PDF output*/ +@d pdf_dest_margin_code (pdftex_first_dimen_code+3) /*dest margin in the PDF output*/ +@d pdf_thread_margin_code (pdftex_first_dimen_code+4) /*thread margin in the PDF output*/ +@d pdf_first_line_height_code (pdftex_first_dimen_code+5) +@d pdf_last_line_depth_code (pdftex_first_dimen_code+6) +@d pdf_each_line_height_code (pdftex_first_dimen_code+7) +@d pdf_each_line_depth_code (pdftex_first_dimen_code+8) +@d pdf_ignored_dimen_code (pdftex_first_dimen_code+9) +@d pdf_px_dimen_code (pdftex_first_dimen_code+10) +@d pdftex_last_dimen_code (pdftex_first_dimen_code+10) /*last number defined in this section*/ +@d dimen_pars (pdftex_last_dimen_code+1) /*total number of dimension parameters*/ + +@<Put each...@>= +if (pdf_on) +{ +primitive("pdfpagesattr", assign_toks, pdf_pages_attr_loc); +@!@:pdf\_pages\_attr\_}{\.{\\pdfpagesattr} primitive@> +primitive("pdfpageattr", assign_toks, pdf_page_attr_loc); +@!@:pdf\_page\_attr\_}{\.{\\pdfpageattr} primitive@> +primitive("pdfpageresources", assign_toks, pdf_page_resources_loc); +@!@:pdf\_page\_resources\_}{\.{\\pdfpageresources} primitive@> +primitive("pdfpkmode", assign_toks, pdf_pk_mode_loc); +@!@:pdf\_pk\_mode\_}{\.{\\pdfpkmode} primitive@> +@# +primitive("pdfoutput", assign_int, int_base+pdf_output_code);@/ +@!@:pdf\_output\_}{\.{\\pdfoutput} primitive@> +primitive("pdfcompresslevel", assign_int, int_base+pdf_compress_level_code);@/ +@!@:pdf\_compress\_level\_}{\.{\\pdfcompresslevel} primitive@> +primitive("pdfobjcompresslevel", assign_int, int_base+pdf_objcompresslevel_code);@/ +@!@:pdf\_objcompresslevel\_}{\.{\\pdfobjcompresslevel} primitive@> +primitive("pdfdecimaldigits", assign_int, int_base+pdf_decimal_digits_code);@/ +@!@:pdf\_decimal\_digits\_}{\.{\\pdfdecimaldigits} primitive@> +primitive("pdfmovechars", assign_int, int_base+pdf_move_chars_code);@/ +@!@:pdf\_move\_chars\_}{\.{\\pdfmovechars} primitive@> +primitive("pdfimageresolution", assign_int, int_base+pdf_image_resolution_code);@/ +@!@:pdf\_image\_resolution\_}{\.{\\pdfimageresolution} primitive@> +primitive("pdfpkresolution", assign_int, int_base+pdf_pk_resolution_code);@/ +@!@:pdf\_pk\_resolution\_}{\.{\\pdfpkresolution} primitive@> +primitive("pdfuniqueresname", assign_int, int_base+pdf_unique_resname_code);@/ +@!@:pdf\_unique\_resname\_}{\.{\\pdfuniqueresname} primitive@> +primitive("pdfoptionpdfminorversion", assign_int, int_base+pdf_minor_version_code);@/ +@!@:pdf\_minor\_version\_}{\.{\\pdfoptionpdfminorversion} primitive@> +primitive("pdfoptionalwaysusepdfpagebox", assign_int, int_base+pdf_option_always_use_pdfpagebox_code);@/ +@!@:pdf\_option\_always\_use\_pdfpagebox\_}{\.{\\pdfoptionalwaysusepdfpagebox} primitive@> +primitive("pdfoptionpdfinclusionerrorlevel", assign_int, int_base+pdf_option_pdf_inclusion_errorlevel_code);@/ +@!@:pdf\_option\_pdf\_inclusion\_errorlevel\_}{\.{\\pdfoptionpdfinclusionerrorlevel} primitive@> +primitive("pdfmajorversion", assign_int, int_base+pdf_major_version_code);@/ +@!@:pdf\_major\_version\_}{\.{\\pdfmajorversion} primitive@> +primitive("pdfminorversion", assign_int, int_base+pdf_minor_version_code);@/ +@!@:pdf\_minor\_version\_}{\.{\\pdfminorversion} primitive@> +primitive("pdfforcepagebox", assign_int, int_base+pdf_force_pagebox_code);@/ +@!@:pdf\_force\_pagebox\_}{\.{\\pdfforcepagebox} primitive@> +primitive("pdfpagebox", assign_int, int_base+pdf_pagebox_code);@/ +@!@:pdf\_pagebox\_}{\.{\\pdfpagebox} primitive@> +primitive("pdfinclusionerrorlevel", assign_int, int_base+pdf_inclusion_errorlevel_code);@/ +@!@:pdf\_inclusion\_errorlevel\_}{\.{\\pdfinclusionerrorlevel} primitive@> +primitive("pdfgamma", assign_int, int_base+pdf_gamma_code);@/ +@!@:pdf\_gamma\_}{\.{\\pdfgamma} primitive@> +@!@:pdf\_gamma\_}{\.{\\pdfgamma} primitive@> +primitive("pdfimagegamma", assign_int, int_base+pdf_image_gamma_code);@/ +@!@:pdf\_image\_gamma\_}{\.{\\pdfimagegamma} primitive@> +primitive("pdfimagehicolor", assign_int, int_base+pdf_image_hicolor_code);@/ +@!@:pdf\_image\_hicolor\_}{\.{\\pdfimagehicolor} primitive@> +primitive("pdfimageapplygamma", assign_int, int_base+pdf_image_apply_gamma_code);@/ +@!@:pdf\_image\_apply\_gamma\_}{\.{\\pdfimageapplygamma} primitive@> +primitive("pdfadjustspacing", assign_int, int_base+pdf_adjust_spacing_code);@/ +@!@:pdf\_adjust\_spacing\_}{\.{\\pdfadjustspacing} primitive@> +primitive("pdfprotrudechars", assign_int, int_base+pdf_protrude_chars_code);@/ +@!@:pdf\_protrude\_chars\_}{\.{\\pdfprotrudechars} primitive@> +primitive("pdftracingfonts", assign_int, int_base+pdf_tracing_fonts_code);@/ +@!@:pdf\_tracing\_fonts\_}{\.{\\pdftracingfonts} primitive@> +primitive("pdfadjustinterwordglue", assign_int, int_base+pdf_adjust_interword_glue_code);@/ +@!@:pdf\_adjust\_interword\_glue\_}{\.{\\pdfadjustinterwordglue} primitive@> +primitive("pdfprependkern", assign_int, int_base+pdf_prepend_kern_code);@/ +@!@:pdf\_prepend\_kern\_}{\.{\\pdfprependkern} primitive@> +primitive("pdfappendkern", assign_int, int_base+pdf_append_kern_code);@/ +@!@:pdf\_append\_kern\_}{\.{\\pdfappendkern} primitive@> +primitive("pdfgentounicode", assign_int, int_base+pdf_gen_tounicode_code);@/ +@!@:pdf\_gen\_tounicode\_}{\.{\\pdfgentounicode} primitive@> +primitive("pdfdraftmode", assign_int, int_base+pdf_draftmode_code);@/ +@!@:pdf\_draftmode\_}{\.{\\pdfdraftmode} primitive@> +primitive("pdfinclusioncopyfonts", assign_int, int_base+pdf_inclusion_copy_font_code);@/ +@!@:pdf\_inclusion\_copy\_font\_}{\.{\\pdfinclusioncopyfonts} primitive@> +primitive("pdfsuppresswarningdupdest", assign_int, int_base+pdf_suppress_warning_dup_dest_code);@/ +@!@:pdf\_suppress\_warning\_dup\_dest\_}{\.{\\pdfsuppresswarningdupdest} primitive@> +primitive("pdfsuppresswarningdupmap", assign_int, int_base+pdf_suppress_warning_dup_map_code);@/ +@!@:pdf\_suppress\_warning\_dup\_map\_}{\.{\\pdfsuppresswarningdupmap} primitive@> +primitive("pdfsuppresswarningpagegroup", assign_int, int_base+pdf_suppress_warning_page_group_code);@/ +@!@:pdf\_suppress\_warning\_page\_group\_}{\.{\\pdfsuppresswarningpagegroup} primitive@> +primitive("pdfinfoomitdate", assign_int, int_base+pdf_info_omit_date_code);@/ +@!@:pdf\_info\_omit\_date\_}{\.{\\pdfinfoomitdate} primitive@> +primitive("pdfsuppressptexinfo", assign_int, int_base+pdf_suppress_ptex_info_code);@/ +@!@:pdf\_suppress\_ptex\_info\_}{\.{\\pdfsuppressptexinfo} primitive@> +primitive("pdfomitcharset", assign_int, int_base+pdf_omit_charset_code);@/ +@!@:pdf\_omit\_charset}{\.{\\pdfomitcharset} primitive@> +primitive("pdfomitinfodict", assign_int, int_base+pdf_omit_info_dict_code);@/ +@!@:pdf\_omit\_info\_dict}{\.{\\pdfomitinfodict} primitive@> +primitive("pdfomitprocset", assign_int, int_base+pdf_omit_procset_code);@/ +@!@:pdf\_omit\_procset}{\.{\\pdfomitprocset} primitive@> +@# +primitive("pdfhorigin", assign_dimen, dimen_base+pdf_h_origin_code);@/ +@!@:pdf\_h\_origin\_}{\.{\\pdfhorigin} primitive@> +primitive("pdfvorigin", assign_dimen, dimen_base+pdf_v_origin_code);@/ +@!@:pdf\_v\_origin\_}{\.{\\pdfvorigin} primitive@> +primitive("pdflinkmargin", assign_dimen, dimen_base+pdf_link_margin_code);@/ +@!@:pdf\_link\_margin\_}{\.{\\pdflinkmargin} primitive@> +primitive("pdfdestmargin", assign_dimen, dimen_base+pdf_dest_margin_code);@/ +@!@:pdf\_dest\_margin\_}{\.{\\pdfdestmargin} primitive@> +primitive("pdfthreadmargin", assign_dimen, dimen_base+pdf_thread_margin_code);@/ +@!@:pdf\_thread\_margin\_}{\.{\\pdfthreadmargin} primitive@> +primitive("pdffirstlineheight", assign_dimen, dimen_base+pdf_first_line_height_code);@/ +@!@:pdf\_first\_line\_height\_}{\.{\\pdffirstlineheight} primitive@> +primitive("pdflastlinedepth", assign_dimen, dimen_base+pdf_last_line_depth_code);@/ +@!@:pdf\_last\_line\_depth\_}{\.{\\pdflastlinedepth} primitive@> +primitive("pdfeachlineheight", assign_dimen, dimen_base+pdf_each_line_height_code);@/ +@!@:pdf\_each\_line\_height\_}{\.{\\pdfeachlineheight} primitive@> +primitive("pdfeachlinedepth", assign_dimen, dimen_base+pdf_each_line_depth_code);@/ +@!@:pdf\_each\_line\_depth\_}{\.{\\pdfeachlinedepth} primitive@> +primitive("pdfignoreddimen", assign_dimen, dimen_base+pdf_ignored_dimen_code);@/ +@!@:pdf\_ignored\_dimen\_}{\.{\\pdfignoreddimen} primitive@> +primitive("pdfpxdimen", assign_dimen, dimen_base+pdf_px_dimen_code);@/ +@!@:pdf\_px\_dimen\_}{\.{\\pdfpxdimen} primitive@> +primitive("ifpdfprimitive", if_test, if_primitive_code); +@!@:if\_primitive\_}{\.{\\if} primitive@> + + +} + +@ As before we add cases to the printing routines. + +@<Cases of |assign_toks| for |print_cmd_chr|@>= + case pdf_pages_attr_loc: print_esc("pdfpagesattr");@+break; + case pdf_page_attr_loc: print_esc("pdfpageattr");@+break; + case pdf_page_resources_loc: print_esc("pdfpageresources");@+break; + case pdf_pk_mode_loc: print_esc("pdfpkmode");@+break; + +@ @<Cases for |print_param|@>= +case pdf_output_code: print_esc("pdfoutput");@+break; +case pdf_compress_level_code: print_esc("pdfcompresslevel");@+break; +case pdf_objcompresslevel_code: print_esc("pdfobjcompresslevel");@+break; +case pdf_decimal_digits_code: print_esc("pdfdecimaldigits");@+break; +case pdf_move_chars_code: print_esc("pdfmovechars");@+break; +case pdf_image_resolution_code: print_esc("pdfimageresolution");@+break; +case pdf_pk_resolution_code: print_esc("pdfpkresolution");@+break; +case pdf_unique_resname_code: print_esc("pdfuniqueresname");@+break; +case pdf_option_always_use_pdfpagebox_code: print_esc("pdfoptionalwaysusepdfpagebox");@+break; +case pdf_option_pdf_inclusion_errorlevel_code: print_esc("pdfoptionpdfinclusionerrorlevel");@+break; +case pdf_major_version_code: print_esc("pdfmajorversion");@+break; +case pdf_minor_version_code: print_esc("pdfminorversion");@+break; +case pdf_force_pagebox_code: print_esc("pdfforcepagebox");@+break; +case pdf_pagebox_code: print_esc("pdfpagebox");@+break; +case pdf_inclusion_errorlevel_code: print_esc("pdfinclusionerrorlevel");@+break; +case pdf_gamma_code: print_esc("pdfgamma");@+break; +case pdf_image_gamma_code: print_esc("pdfimagegamma");@+break; +case pdf_image_hicolor_code: print_esc("pdfimagehicolor");@+break; +case pdf_image_apply_gamma_code: print_esc("pdfimageapplygamma");@+break; +case pdf_adjust_spacing_code: print_esc("pdfadjustspacing");@+break; +case pdf_protrude_chars_code: print_esc("pdfprotrudechars");@+break; +case pdf_tracing_fonts_code: print_esc("pdftracingfonts");@+break; +case pdf_adjust_interword_glue_code: print_esc("pdfadjustinterwordglue");@+break; +case pdf_prepend_kern_code: print_esc("pdfprependkern");@+break; +case pdf_append_kern_code: print_esc("pdfappendkern");@+break; +case pdf_gen_tounicode_code: print_esc("pdfgentounicode");@+break; +case pdf_draftmode_code: print_esc("pdfdraftmode");@+break; +case pdf_inclusion_copy_font_code: print_esc("pdfinclusioncopyfonts");@+break; +case pdf_suppress_warning_dup_dest_code: print_esc("pdfsuppresswarningdupdest");@+break; +case pdf_suppress_warning_dup_map_code: print_esc("pdfsuppresswarningdupmap");@+break; +case pdf_suppress_warning_page_group_code: print_esc("pdfsuppresswarningpagegroup");@+break; +case pdf_info_omit_date_code: print_esc("pdfinfoomitdate");@+break; +case pdf_suppress_ptex_info_code: print_esc("pdfsuppressptexinfo");@+break; +case pdf_omit_charset_code: print_esc("pdfomitcharset");@+break; +case pdf_omit_info_dict_code: print_esc("pdfomitinfodict");@+break; +case pdf_omit_procset_code: print_esc("pdfomitprocset");@+break; + +@ @<Cases for |print_lenght_param|@>= +case pdf_h_origin_code: print_esc("pdfhorigin");@+break; +case pdf_v_origin_code: print_esc("pdfvorigin");@+break; +case pdf_link_margin_code: print_esc("pdflinkmargin");@+break; +case pdf_dest_margin_code: print_esc("pdfdestmargin");@+break; +case pdf_thread_margin_code: print_esc("pdfthreadmargin");@+break; +case pdf_first_line_height_code: print_esc("pdffirstlineheight");@+break; +case pdf_last_line_depth_code: print_esc("pdflastlinedepth");@+break; +case pdf_each_line_height_code: print_esc("pdfeachlineheight");@+break; +case pdf_each_line_depth_code: print_esc("pdfeachlinedepth");@+break; +case pdf_ignored_dimen_code: print_esc("pdfignoreddimen");@+break; +case pdf_px_dimen_code: print_esc("pdfpxdimen");@+break; + +@ It remains to initialize some of these entries. + +@<Initialize table entries...@>= +dimen_par(pdf_h_origin_code)=4736287; /* 0.5 inch */ +dimen_par(pdf_v_origin_code)=4736287; +int_par(pdf_compress_level_code)=9; +int_par(pdf_objcompresslevel_code)=0; +int_par(pdf_decimal_digits_code)=3; +int_par(pdf_image_resolution_code)=72; +int_par(pdf_major_version_code)=1; +int_par(pdf_minor_version_code)=4; +int_par(pdf_gamma_code)=1000; +int_par(pdf_image_gamma_code)=2200; +int_par(pdf_image_hicolor_code)=1; +int_par(pdf_image_apply_gamma_code)=0; +int_par(pdf_draftmode_code)=0; +dimen_par(pdf_px_dimen_code)=65782; /* 1bp */ + +@ The next class of primitives extend the |convert| comand. +These codes go in between |Prote_last_convert_code| and |job_name_code|. + +@d pdftex_first_convert_code (Prote_last_convert_code+1) /*base for \pdfTeX's command codes*/ +@d pdftex_revision_code (pdftex_first_convert_code+0) /*command code for \.{\\pdftexrevision}*/ +@d pdftex_banner_code (pdftex_first_convert_code+1) /*command code for \.{\\pdftexbanner}*/ +@d pdf_font_name_code (pdftex_first_convert_code+2) /*command code for \.{\\pdffontname}*/ +@d pdf_font_objnum_code (pdftex_first_convert_code+3) /*command code for \.{\\pdffontobjnum}*/ +@d pdf_font_size_code (pdftex_first_convert_code+4) /*command code for \.{\\pdffontsize}*/ +@d pdf_page_ref_code (pdftex_first_convert_code+5) /*command code for \.{\\pdfpageref}*/ +@d pdf_xform_name_code (pdftex_first_convert_code+6) /*command code for \.{\\pdfxformname}*/ +@d pdf_escape_string_code (pdftex_first_convert_code+7) /*command code for \.{\\pdfescapestring}*/ +@d pdf_escape_name_code (pdftex_first_convert_code+8) /*command code for \.{\\pdfescapename}*/ +@d left_margin_kern_code (pdftex_first_convert_code+9) /*command code for \.{\\leftmarginkern}*/ +@d right_margin_kern_code (pdftex_first_convert_code+10) /*command code for \.{\\rightmarginkern}*/ +@d pdf_colorstack_init_code (pdftex_first_convert_code+11) /*command code for \.{\\pdfcolorstackinit}*/ +@d pdf_escape_hex_code (pdftex_first_convert_code+12) /*command code for \.{\\pdfescapehex}*/ +@d pdf_unescape_hex_code (pdftex_first_convert_code+13) /*command code for \.{\\pdfunescapehex}*/ +@d pdf_match_code (pdftex_first_convert_code+14) /*command code for \.{\\pdfmatch}*/ +@d pdf_last_match_code (pdftex_first_convert_code+15) /*command code for \.{\\pdflastmatch}*/ +@d pdf_insert_ht_code (pdftex_first_convert_code+16) /*command code for \.{\\pdfinsertht}*/ +@d pdf_ximage_bbox_code (pdftex_first_convert_code+17) /*command code for \.{\\pdfximagebbox}*/ +@d pdftex_convert_codes (pdftex_first_convert_code+18) /*end of \pdfTeX's command codes*/ + + +@<Put each...@>= +if (pdf_on) +{ +primitive("pdftexrevision", convert, pdftex_revision_code);@/ +@!@:pdftex\_revision\_}{\.{\\pdftexrevision} primitive@> +primitive("pdftexbanner", convert, pdftex_banner_code);@/ +@!@:pdftex\_banner\_}{\.{\\pdftexbanner} primitive@> +primitive("pdffontname", convert, pdf_font_name_code);@/ +@!@:pdf\_font\_name\_}{\.{\\pdffontname} primitive@> +primitive("pdffontobjnum", convert, pdf_font_objnum_code);@/ +@!@:pdf\_font\_objnum\_}{\.{\\pdffontobjnum} primitive@> +primitive("pdffontsize", convert, pdf_font_size_code);@/ +@!@:pdf\_font\_size\_}{\.{\\pdffontsize} primitive@> +primitive("pdfpageref", convert, pdf_page_ref_code);@/ +@!@:pdf\_page\_ref\_}{\.{\\pdfpageref} primitive@> +primitive("leftmarginkern", convert, left_margin_kern_code);@/ +@!@:left\_margin\_kern\_}{\.{\\leftmarginkern} primitive@> +primitive("rightmarginkern", convert, right_margin_kern_code);@/ +@!@:right\_margin\_kern\_}{\.{\\rightmarginkern} primitive@> +primitive("pdfxformname", convert, pdf_xform_name_code);@/ +@!@:pdf\_xform\_name\_}{\.{\\pdfxformname} primitive@> +primitive("pdfescapestring", convert, pdf_escape_string_code);@/ +@!@:pdf\_escape\_string\_}{\.{\\pdfescapestring} primitive@> +primitive("pdfescapename", convert, pdf_escape_name_code);@/ +@!@:pdf\_escape\_name\_}{\.{\\pdfescapename} primitive@> +primitive("pdfescapehex", convert, pdf_escape_hex_code);@/ +@!@:pdf\_escape\_hex\_}{\.{\\pdfescapehex} primitive@> +primitive("pdfunescapehex", convert, pdf_unescape_hex_code);@/ +@!@:pdf\_unescape\_hex\_}{\.{\\pdfunescapehex} primitive@> +primitive("pdfmatch", convert, pdf_match_code);@/ +@!@:pdf\_match\_}{\.{\\pdfmatch} primitive@> +primitive("pdflastmatch", convert, pdf_last_match_code);@/ +@!@:pdf\_last\_match\_}{\.{\\pdflastmatch} primitive@> +primitive("pdfcolorstackinit", convert, pdf_colorstack_init_code);@/ +@!@:pdf\_colorstack\_init\_}{\.{\\pdfcolorstackinit} primitive@> +} + +@ The usual code for printing: + +@<Cases of |convert| for |print_cmd_chr|@>= + case pdftex_revision_code: print_esc("pdftexrevision");@+break; + case pdftex_banner_code: print_esc("pdftexbanner");@+break; + case pdf_font_name_code: print_esc("pdffontname");@+break; + case pdf_font_objnum_code: print_esc("pdffontobjnum");@+break; + case pdf_font_size_code: print_esc("pdffontsize");@+break; + case pdf_page_ref_code: print_esc("pdfpageref");@+break; + case left_margin_kern_code: print_esc("leftmarginkern");@+break; + case right_margin_kern_code: print_esc("rightmarginkern");@+break; + case pdf_xform_name_code: print_esc("pdfxformname");@+break; + case pdf_escape_string_code: print_esc("pdfescapestring");@+break; + case pdf_escape_name_code: print_esc("pdfescapename");@+break; + case pdf_escape_hex_code: print_esc("pdfescapehex");@+break; + case pdf_unescape_hex_code: print_esc("pdfunescapehex");@+break; + case pdf_match_code: print_esc("pdfmatch");@+break; + case pdf_last_match_code: print_esc("pdflastmatch");@+break; + case pdf_colorstack_init_code: print_esc("pdfcolorstackinit");@+break; + case pdf_insert_ht_code: print_esc("pdfinsertht");@+break; + case pdf_ximage_bbox_code: print_esc("pdfximagebbox");@+break; + +@ The following code is taken from pdftex. At this point, I should drop +some of the primitives that I do not want to implement. +To skip general text we use the following macro + + +@<Cases of `Scan the argument for command |c|'@>= +case pdftex_revision_code: do_nothing;@+break; +case pdftex_banner_code: do_nothing;@+break; +case pdf_font_name_code: case pdf_font_objnum_code: + case pdf_font_size_code: {@+ + scan_font_ident(); + if (cur_val==null_font) + pdf_error("font","invalid font identifier"); +} @+break; +case pdf_page_ref_code: {@+ + scan_int(); + if (cur_val <= 0) + pdf_error("pageref","invalid page number"); +} @+break; +case left_margin_kern_code: case right_margin_kern_code: {@+ + pointer p; + scan_register_num(); + fetch_box(p); + if ((p==null)||(type(p)!=hlist_node)) + pdf_error("marginkern","a non-empty hbox expected"); +} @+break; +case pdf_xform_name_code: {@+ + scan_int(); +} @+break; +case pdf_escape_string_code: +case pdf_escape_name_code: +case pdf_escape_hex_code: +case pdf_unescape_hex_code: + scan_general_x_text(); /*the simplified version*/ + ins_list(link(link(garbage))); + free_avail(link(garbage)); /*drop reference count*/ + return; +case pdf_match_code: + scan_keyword("icase"); + if (scan_keyword("subcount")) scan_int(); + scan_pdf_ext_toks(); /*the simplified version*/ + scan_pdf_ext_toks(); /*the simplified version*/ + break; +case pdf_last_match_code: + {@+pointer p,q; + scan_int(); + /* always returning ``\.{-1->}''*/ + p=temp_head;link(p)=null; + store_new_token(other_token+'-'); + store_new_token(other_token+'1'); + store_new_token(other_token+'-'); + store_new_token(other_token+'>'); + ins_list(link(temp_head)); + return; + } +case pdf_colorstack_init_code: + {@+ + scan_keyword("page"); + if (!scan_keyword("direct")) + scan_keyword("page"); + scan_pdf_ext_toks(); /*the simplified version*/ + cur_val=0; + } @+break; +case pdf_insert_ht_code: scan_register_num();@+break; +case pdf_ximage_bbox_code: {@+ + scan_int(); + scan_int(); +}@+break; + + +@ This is the ``dummy'' |scan_pdf_ext_toks| routine we used above +and a variation of it which we need later. + +@<Declare procedures needed in |do_ext...@>= +static void scan_pdf_ext_toks(void) +{ scan_toks(false,true); /*like \.{\\special}*/ + flush_list(def_ref); +} +static void scan_pdf_ext_late_toks(void) +{ scan_toks(false, false); /*like \.{\\special}, but doesn't expand*/ + flush_list(def_ref); +} + + +@ The second part of handling conversion commands is the printing of the result. + +@<Cases of `Print the result of command |c|'@>= +case pdftex_revision_code: print(pdftex_revision);@+break; +case pdftex_banner_code: print("This is pdfTeX, Version 3.141592653");@+break; +case pdf_font_name_code: case pdf_font_objnum_code: print_int(cur_val);@+break; +case pdf_font_size_code: + print_scaled(font_size[cur_val]); + print("pt");@+break; +case pdf_page_ref_code: print_int(1);@+break; +case pdf_xform_name_code: print_int(cur_val);@+break; +case pdf_escape_string_code: +case pdf_escape_name_code: +case left_margin_kern_code: print("0pt");@+break; +case right_margin_kern_code: print("0pt");@+break; +case pdf_colorstack_init_code: print_int(cur_val);@+break; +case pdf_escape_hex_code: +case pdf_unescape_hex_code: +case pdf_match_code: +case pdf_last_match_code: +case pdf_insert_ht_code: print("10pt");@+break; +case pdf_ximage_bbox_code: print("10pt");@+break; + + +@ A few functions are neede to complete the implementation of the \pdfTeX\ simulation. + + +@<Error handling procedures@>= +static void pdf_error(char *t, char *p) +{@+ + normalize_selector(); + print_err("pdfTeX error"); + if (t!=0) {@+ + print(" ("); + print(t); + print(")"); + } + print(": ");print(p); + succumb; +} + + +@ The last command we need to consider is the |extension| command. + +@d pdf_literal_node (pdftex_first_extension_code+0) + +@d pdf_lateliteral_node (pdftex_first_extension_code+1) /*Not needed*/ + +@d pdf_obj_code (pdftex_first_extension_code+2) +@d pdf_refobj_node (pdftex_first_extension_code+3) +@d pdf_xform_code (pdftex_first_extension_code+4) +@d pdf_refxform_node (pdftex_first_extension_code+5) +@d pdf_ximage_code (pdftex_first_extension_code+6) +@d pdf_refximage_node (pdftex_first_extension_code+7) +@d pdf_annot_node (pdftex_first_extension_code+8) +@d pdf_start_link_node (pdftex_first_extension_code+9) +@d pdf_end_link_node (pdftex_first_extension_code+10) +@d pdf_outline_code (pdftex_first_extension_code+11) +@d pdf_dest_node (pdftex_first_extension_code+12) +@d pdf_thread_node (pdftex_first_extension_code+13) +@d pdf_start_thread_node (pdftex_first_extension_code+14) +@d pdf_end_thread_node (pdftex_first_extension_code+15) + +@d pdf_info_code (pdftex_first_extension_code+16) +@d pdf_catalog_code (pdftex_first_extension_code+17) +@d pdf_names_code (pdftex_first_extension_code+18) + +@d pdf_font_attr_code (pdftex_first_extension_code+19) +@d pdf_include_chars_code (pdftex_first_extension_code+20) +@d pdf_map_file_code (pdftex_first_extension_code+21) +@d pdf_map_line_code (pdftex_first_extension_code+22) +@d pdf_trailer_code (pdftex_first_extension_code+23) +@d pdf_trailer_id_code (pdftex_first_extension_code+24) + +@d pdf_font_expand_code (pdftex_first_extension_code+25) + +@d pdf_snap_ref_point_node (pdftex_first_extension_code+26) +@d pdf_snapy_node (pdftex_first_extension_code+27) +@d pdf_snapy_comp_node (pdftex_first_extension_code+28) +@d pdf_glyph_to_unicode_code (pdftex_first_extension_code+29) +@d pdf_colorstack_node (pdftex_first_extension_code+30) +@d pdf_setmatrix_node (pdftex_first_extension_code+31) +@d pdf_save_node (pdftex_first_extension_code+32) +@d pdf_restore_node (pdftex_first_extension_code+33) +@d pdf_nobuiltin_tounicode_code (pdftex_first_extension_code+34) +@d pdf_interword_space_on_node (pdftex_first_extension_code+35) +@d pdf_interword_space_off_node (pdftex_first_extension_code+36) +@d pdf_fake_space_node (pdftex_first_extension_code+37) +@d pdf_running_link_off_node (pdftex_first_extension_code+38) +@d pdf_running_link_on_node (pdftex_first_extension_code+39) +@d pdf_space_font_code (pdftex_first_extension_code+40) +@# +@d tracing_stack_level_code (pdftex_first_extension_code+41) +@d pdf_par_token_context_code (pdftex_first_extension_code+42) +@d pdf_par_token_name_code (pdftex_first_extension_code+43) +@d pdf_show_stream_code (pdftex_first_extension_code+44) +@# +@d pdftex_last_extension_code (pdftex_first_extension_code+44) + +@<Put each...@>= +if (pdf_on) +{ +primitive("pdfliteral", extension, pdf_literal_node);@/ +@!@:pdf\_literal\_}{\.{\\pdfliteral} primitive@> +primitive("pdfobj", extension, pdf_obj_code);@/ +@!@:pdf\_obj\_}{\.{\\pdfobj} primitive@> +primitive("pdfrefobj", extension, pdf_refobj_node);@/ +@!@:pdf\_refobj\_}{\.{\\pdfrefobj} primitive@> +primitive("pdfxform", extension, pdf_xform_code);@/ +@!@:pdf\_xform\_}{\.{\\pdfxform} primitive@> +primitive("pdfrefxform", extension, pdf_refxform_node);@/ +@!@:pdf\_refxform\_}{\.{\\pdfrefxform} primitive@> +primitive("pdfximage", extension, pdf_ximage_code);@/ +@!@:pdf\_ximage\_}{\.{\\pdfximage} primitive@> +primitive("pdfrefximage", extension, pdf_refximage_node);@/ +@!@:pdf\_refximage\_}{\.{\\pdfrefximage} primitive@> +primitive("pdfannot", extension, pdf_annot_node);@/ +@!@:pdf\_annot\_}{\.{\\pdfannot} primitive@> +primitive("pdfstartlink", extension, pdf_start_link_node);@/ +@!@:pdf\_start\_link\_}{\.{\\pdfstartlink} primitive@> +primitive("pdfendlink", extension, pdf_end_link_node);@/ +@!@:pdf\_end\_link\_}{\.{\\pdfendlink} primitive@> +primitive("pdfoutline", extension, pdf_outline_code);@/ +@!@:pdf\_outline\_}{\.{\\pdfoutline} primitive@> +primitive("pdfdest", extension, pdf_dest_node);@/ +@!@:pdf\_dest\_}{\.{\\pdfdest} primitive@> +primitive("pdfthread", extension, pdf_thread_node);@/ +@!@:pdf\_thread\_}{\.{\\pdfthread} primitive@> +primitive("pdfstartthread", extension, pdf_start_thread_node);@/ +@!@:pdf\_start\_thread\_}{\.{\\pdfstartthread} primitive@> +primitive("pdfendthread", extension, pdf_end_thread_node);@/ +@!@:pdf\_end\_thread\_}{\.{\\pdfendthread} primitive@> + +primitive("pdfinfo", extension, pdf_info_code);@/ +@!@:pdf\_info\_}{\.{\\pdfinfo} primitive@> +primitive("pdfcatalog", extension, pdf_catalog_code);@/ +@!@:pdf\_catalog\_}{\.{\\pdfcatalog} primitive@> +primitive("pdfnames", extension, pdf_names_code);@/ +@!@:pdf\_names\_}{\.{\\pdfnames} primitive@> + +primitive("pdffontattr", extension, pdf_font_attr_code);@/ +@!@:pdf\_font\_attr\_}{\.{\\pdffontattr} primitive@> +primitive("pdfincludechars", extension, pdf_include_chars_code);@/ +@!@:pdf\_include\_chars\_}{\.{\\pdfincludechars} primitive@> +primitive("pdfmapfile", extension, pdf_map_file_code);@/ +@!@:pdf\_map\_file\_}{\.{\\pdfmapfile} primitive@> +primitive("pdfmapline", extension, pdf_map_line_code);@/ +@!@:pdf\_map\_line\_}{\.{\\pdfmapline} primitive@> +primitive("pdftrailer", extension, pdf_trailer_code);@/ +@!@:pdf\_trailer\_}{\.{\\pdftrailer} primitive@> +primitive("pdftrailerid", extension, pdf_trailer_id_code);@/ +@!@:pdf\_trailer\_id\_}{\.{\\pdftrailerid} primitive@> + +primitive("pdffontexpand", extension, pdf_font_expand_code);@/ +@!@:pdf\_font\_expand\_}{\.{\\pdffontexpand} primitive@> + +primitive("pdfsnaprefpoint", extension, pdf_snap_ref_point_node);@/ +@!@:pdf\_snap\_ref\_point\_}{\.{\\pdfsnaprefpoint} primitive@> +primitive("pdfsnapy", extension, pdf_snapy_node);@/ +@!@:pdf\_snapy\_}{\.{\\pdfsnapy} primitive@> +primitive("pdfsnapycomp", extension, pdf_snapy_comp_node);@/ +@!@:pdf\_snapy\_comp\_}{\.{\\pdfsnapycomp} primitive@> +primitive("pdfglyphtounicode", extension, pdf_glyph_to_unicode_code);@/ +@!@:pdf\_glyph\_to\_unicode\_}{\.{\\pdfglyphtounicode} primitive@> +primitive("pdfcolorstack", extension, pdf_colorstack_node);@/ +@!@:pdf\_colorstack\_}{\.{\\pdfcolorstack} primitive@> +primitive("pdfsetmatrix", extension, pdf_setmatrix_node);@/ +@!@:pdf\_setmatrix\_}{\.{\\pdfsetmatrix} primitive@> +primitive("pdfsave", extension, pdf_save_node);@/ +@!@:pdf\_save\_}{\.{\\pdfsave} primitive@> +primitive("pdfrestore", extension, pdf_restore_node);@/ +@!@:pdf\_restore\_}{\.{\\pdfrestore} primitive@> +primitive("pdfnobuiltintounicode", extension, pdf_nobuiltin_tounicode_code);@/ +@!@:pdf\_nobuiltin\_tounicode\_}{\.{\\pdfnobuiltintounicode} primitive@> +primitive("pdfinterwordspaceon", extension, pdf_interword_space_on_node);@/ +@!@:pdf\_interword\_space\_on\_}{\.{\\pdfinterwordspaceon} primitive@> +primitive("pdfinterwordspaceoff", extension, pdf_interword_space_off_node);@/ +@!@:pdf\_interword\_space\_off\_}{\.{\\pdfinterwordspaceoff} primitive@> +primitive("pdffakespace", extension, pdf_fake_space_node);@/ +@!@:pdf\_fake\_space\_}{\.{\\pdffakespace} primitive@> +primitive("pdfrunninglinkoff", extension, pdf_running_link_off_node);@/ +@!@:pdf\_running\_link\_off\_}{\.{\\pdfrunninglinkoff} primitive@> +primitive("pdfrunninglinkon", extension, pdf_running_link_on_node);@/ +@!@:pdf\_running\_link\_on\_}{\.{\\pdfrunninglinkon} primitive@> +primitive("pdfspacefont", extension, pdf_space_font_code);@/ +@!@:pdf\_space\_font\_}{\.{\\pdfspacefont} primitive@> +@# +primitive("tracingstacklevels", extension, tracing_stack_level_code);@/ +@!@:tracingstacklevels\_}{\.{\\tracingstacklevels} primitive@> +primitive("partokencontext", extension, pdf_par_token_context_code);@/ +@!@:\partokencontext}{\.{\\partokencontext} primitive@> +primitive("partokenname", extension, pdf_par_token_name_code);@/ +@!@:partokenname\_}{\.{\\partokenname} primitive@> +primitive("showstream", extension, pdf_show_stream_code);@/ +@!@:showstream\_}{\.{\\showstream} primitive@> +} + +@ After having defined the primitives, we arange for printing them. + +@<Cases of |extension| for |print_cmd_chr|@>= + case pdf_annot_node: print_esc("pdfannot");@+break; + case pdf_catalog_code: print_esc("pdfcatalog");@+break; + case pdf_dest_node: print_esc("pdfdest");@+break; + case pdf_end_link_node: print_esc("pdfendlink");@+break; + case pdf_end_thread_node: print_esc("pdfendthread");@+break; + case pdf_font_attr_code: print_esc("pdffontattr");@+break; + case pdf_font_expand_code: print_esc("pdffontexpand");@+break; + case pdf_include_chars_code: print_esc("pdfincludechars");@+break; + case pdf_info_code: print_esc("pdfinfo");@+break; + case pdf_literal_node: print_esc("pdfliteral");@+break; + case pdf_colorstack_node: print_esc("pdfcolorstack");@+break; + case pdf_setmatrix_node: print_esc("pdfsetmatrix");@+break; + case pdf_save_node: print_esc("pdfsave");@+break; + case pdf_restore_node: print_esc("pdfrestore");@+break; + case pdf_map_file_code: print_esc("pdfmapfile");@+break; + case pdf_map_line_code: print_esc("pdfmapline");@+break; + case pdf_names_code: print_esc("pdfnames");@+break; + case pdf_obj_code: print_esc("pdfobj");@+break; + case pdf_outline_code: print_esc("pdfoutline");@+break; + case pdf_refobj_node: print_esc("pdfrefobj");@+break; + case pdf_refxform_node: print_esc("pdfrefxform");@+break; + case pdf_refximage_node: print_esc("pdfrefximage");@+break; + case pdf_snap_ref_point_node: print_esc("pdfsnaprefpoint");@+break; + case pdf_snapy_comp_node: print_esc("pdfsnapycomp");@+break; + case pdf_snapy_node: print_esc("pdfsnapy");@+break; + case pdf_start_link_node: print_esc("pdfstartlink");@+break; + case pdf_start_thread_node: print_esc("pdfstartthread");@+break; + case pdf_thread_node: print_esc("pdfthread");@+break; + case pdf_trailer_code: print_esc("pdftrailer");@+break; + case pdf_trailer_id_code: print_esc("pdftrailerid");@+break; + case pdf_xform_code: print_esc("pdfxform");@+break; + case pdf_ximage_code: print_esc("pdfximage");@+break; + case pdf_nobuiltin_tounicode_code: print_esc("pdfnobuiltintounicode");@+break; + case pdf_glyph_to_unicode_code: print_esc("pdfglyphtounicode");@+break; + case pdf_interword_space_on_node: print_esc("pdfinterwordspaceon");@+break; + case pdf_interword_space_off_node: print_esc("pdfinterwordspaceoff");@+break; + case pdf_fake_space_node: print_esc("pdffakespace");@+break; + case pdf_running_link_off_node: print_esc("pdfrunninglinkoff");@+break; + case pdf_running_link_on_node: print_esc("pdfrunninglinkon");@+break; + case pdf_space_font_code: print_esc("pdfspacefont");@+break; + case tracing_stack_level_code: print_esc("tracingstacklevels");@+break; + case pdf_par_token_context_code: print_esc("partokencontext");@+break; + case pdf_par_token_name_code: print_esc("partokenname");@+break; + case pdf_show_stream_code: print_esc("showstream");@+break; + + +@ And last not least we have to implement the primitives. + +@<Cases for |do_extension|@>= + case pdf_annot_node: @<Implement \.{\\pdfannot}@>@;@+break; + case pdf_catalog_code: @<Implement \.{\\pdfcatalog}@>@;@+break; + case pdf_dest_node: @<Implement \.{\\pdfdest}@>@;@+break; + case pdf_end_link_node: @+break; + case pdf_end_thread_node: @+break; + case pdf_font_attr_code: @<Implement \.{\\pdffontattr}@>@;@+break; + case pdf_font_expand_code: read_expand_font();@+break; + case pdf_include_chars_code: pdf_include_chars();@+break; + case pdf_info_code: scan_pdf_ext_toks();@+break; + case pdf_literal_node: @<Implement \.{\\pdfliteral}@>@;@+break; + case pdf_colorstack_node: @<Implement \.{\\pdfcolorstack}@>@;@+break; + case pdf_setmatrix_node: scan_pdf_ext_toks();@+break; + case pdf_save_node: @+break; + case pdf_restore_node: @+break; + case pdf_map_file_code: scan_pdf_ext_toks();@+break; + case pdf_map_line_code: scan_pdf_ext_toks();@+break; + case pdf_names_code: scan_pdf_ext_toks();@+break; + case pdf_obj_code: @<Implement \.{\\pdfobj}@>@;@+break; + case pdf_outline_code: @<Implement \.{\\pdfoutline}@>@;@+break; + case pdf_refobj_node: scan_int();@+break; + case pdf_refxform_node: scan_int();@+break; + case pdf_refximage_node: scan_int();@+break; + case pdf_snap_ref_point_node: @+break; + case pdf_snapy_comp_node: scan_int();@+break; + case pdf_snapy_node: @+break; + case pdf_start_link_node: scan_annot(); scan_action();@+break; + case pdf_start_thread_node: scan_annot(); scan_thread_id();@+break; + case pdf_thread_node: scan_annot(); scan_thread_id();@+break; + case pdf_trailer_code: scan_pdf_ext_toks();@+break; + case pdf_trailer_id_code: scan_pdf_ext_toks();@+break; + case pdf_xform_code: @<Implement \.{\\pdfxform}@>@;@+break; + case pdf_ximage_code: scan_image(); @+break; + case pdf_glyph_to_unicode_code: scan_pdf_ext_toks();scan_pdf_ext_toks();@+break; + case pdf_nobuiltin_tounicode_code: scan_font_ident();@+break; + case pdf_interword_space_on_node: @+break; + case pdf_interword_space_off_node: @+break; + case pdf_fake_space_node: @+break; + case pdf_running_link_off_node: @+break; + case pdf_running_link_on_node: @+break; + case pdf_space_font_code: scan_pdf_ext_toks(); @+break; + case tracing_stack_level_code: scan_int();@+break; + case pdf_par_token_context_code: scan_optional_equals();scan_int();@+break; + case pdf_par_token_name_code: scan_optional_equals();get_token();@+break; + case pdf_show_stream_code: scan_optional_equals();scan_int();@+break; + + +@ @<Implement \.{\\pdfannot}@>= +{@+ if (scan_keyword("reserveobjnum")) {@+@<Scan an optional space@>;} + else{@+ + if (scan_keyword("useobjnum")) {@+scan_int();} + scan_annot(); + scan_pdf_ext_toks(); + } +} + +@ @<Implement \.{\\pdfcatalog}@>= +{@+ scan_pdf_ext_toks(); + if (scan_keyword("openaction")) scan_action(); +} + +@ The |scan_action| routine is used not only here +but also for links and outlines. + + +@d pdf_action_page 0 /*GoTo action*/ +@d pdf_action_goto 1 /*GoTo action*/ +@d pdf_action_thread 2 /*Thread action*/ +@d pdf_action_user 3 /*user-defined action*/ + +@<Declare procedures needed in |do_ext...@>= +static void scan_action(void) /*read an action specification*/ +{@+int pdf_action; + int pdf_action_file=0; + int pdf_action_new_window=0; + if (scan_keyword("user")) + pdf_action=pdf_action_user; + else if (scan_keyword("goto")) + pdf_action=pdf_action_goto; + else if (scan_keyword("thread")) + pdf_action=pdf_action_thread; + else + pdf_error("ext1","action type missing"); + if (pdf_action==pdf_action_user) {@+ + scan_pdf_ext_toks(); + return; } + if (scan_keyword("file")) {@+ + scan_pdf_ext_toks(); + pdf_action_file=1; + } + if (scan_keyword("struct")) {@+ + if (pdf_action!=pdf_action_goto) + pdf_error("ext1","only GoTo action can be used with `struct'"); + if (pdf_action_file!=0) {@+ + scan_pdf_ext_toks(); + } + else if (scan_keyword("name")) {@+ + scan_pdf_ext_toks(); + } + else if (scan_keyword("num")) {@+ + scan_int(); + if (cur_val <= 0) + pdf_error("ext1","num identifier must be positive"); + } + else + pdf_error("ext1","identifier type missing"); + } + if (scan_keyword("page")) {@+ + if (pdf_action!=pdf_action_goto) + pdf_error("ext1","only GoTo action can be used with `page'"); + pdf_action=pdf_action_page; + scan_int(); + if (cur_val <= 0) + pdf_error("ext1","page number must be positive"); + scan_pdf_ext_toks(); + } + else if (scan_keyword("name")) {@+ + scan_pdf_ext_toks(); + } + else if (scan_keyword("num")) {@+ + if ((pdf_action==pdf_action_goto)&& + (pdf_action_file!=0)) + pdf_error("ext1", + "`goto' option cannot be used with both `file' and `num'"); + scan_int(); + if (cur_val <= 0) + pdf_error("ext1","num identifier must be positive"); + } + else + pdf_error("ext1","identifier type missing"); + if (scan_keyword("newwindow")) {@+ + pdf_action_new_window=1; + @<Scan an optional space@>;} + else if (scan_keyword("nonewwindow")) {@+ + pdf_action_new_window=2; + @<Scan an optional space@>;} + else + pdf_action_new_window=0; + if ((pdf_action_new_window > 0)&& + (((pdf_action!=pdf_action_goto)&& + (pdf_action!=pdf_action_page))|| + (pdf_action_file==0))) + pdf_error("ext1", + "`newwindow'/`nonewwindow' must be used with `goto' and `file' option"); +} + +@ Next we scan a destination. + +@d pdf_dest_xyz 0 +@d pdf_dest_fit 1 +@d pdf_dest_fith 2 +@d pdf_dest_fitv 3 +@d pdf_dest_fitb 4 +@d pdf_dest_fitbh 5 +@d pdf_dest_fitbv 6 +@d pdf_dest_fitr 7 + +@<Implement \.{\\pdfdest}@>= +{@+ int pdf_dest_type; + if (scan_keyword("struct")) {@+ + scan_int(); + if (cur_val <= 0) + pdf_error("ext1","struct identifier must be positive"); + } + if (scan_keyword("num")) {@+ + scan_int(); + if (cur_val <= 0) + pdf_error("ext1","num identifier must be positive"); + if (cur_val > max_halfword) + pdf_error("ext1","number too big"); + } + else if (scan_keyword("name")) {@+ + scan_pdf_ext_toks(); + } + else + pdf_error("ext1","identifier type missing"); + if (scan_keyword("xyz")) {@+ + pdf_dest_type=pdf_dest_xyz; + if (scan_keyword("zoom")) {@+ + scan_int(); + if (cur_val > max_halfword) + pdf_error("ext1","number too big"); + } + } + else if (scan_keyword("fitbh")) + pdf_dest_type=pdf_dest_fitbh; + else if (scan_keyword("fitbv")) + pdf_dest_type=pdf_dest_fitbv; + else if (scan_keyword("fitb")) + pdf_dest_type=pdf_dest_fitb; + else if (scan_keyword("fith")) + pdf_dest_type=pdf_dest_fith; + else if (scan_keyword("fitv")) + pdf_dest_type=pdf_dest_fitv; + else if (scan_keyword("fitr")) + pdf_dest_type=pdf_dest_fitr; + else if (scan_keyword("fit")) + pdf_dest_type=pdf_dest_fit; + else + pdf_error("ext1","destination type missing"); + @<Scan an optional space@>; + if (pdf_dest_type==pdf_dest_fitr) {@+ + scan_alt_rule(); /*scans | < rule spec > | to |alt_rule|*/ + } +} + + + +@ @<Declare procedures needed in |do_ext...@>= +static void scan_alt_rule(void) /*scans rule spec to |alt_rule|*/ +{@+ +reswitch: + if (scan_keyword("width")) {@+ + scan_normal_dimen; + goto reswitch; + } + if (scan_keyword("height")) {@+ + scan_normal_dimen; + goto reswitch; + } + if (scan_keyword("depth")) {@+ + scan_normal_dimen; + goto reswitch; + } +} + +static void scan_annot(void) /*scan an annotation*/ +{@+ + scan_alt_rule(); /*scans | < rule spec > |*/ + if (scan_keyword("attr")) + scan_pdf_ext_toks(); +} + + + +@ @<Implement \.{\\pdffontattr}@>= +{@+ + scan_font_ident(); + if (cur_val==null_font) + pdf_error("font","invalid font identifier"); + scan_pdf_ext_toks(); +} + +@ To implement \.{\\pdffontexpand} we need the following routine: + +@<Declare procedures needed in |do_ext...@>= +static void read_expand_font(void) /*read font expansion spec*/ +{@+int shrink_limit, stretch_limit, font_step; + internal_font_number f; + + /*read font expansion parameters*/ + scan_font_ident(); + f=cur_val; + if (f==null_font) + pdf_error("font expansion","invalid font identifier"); + scan_optional_equals(); + scan_int(); + scan_int(); + scan_int(); + if (scan_keyword("autoexpand")) {@+ + @<Scan an optional space@>; + } +} + +@ Do implement \.{\\pdfincludechars} we need the next function. +@<Declare procedures needed in |do_ext...@>= +static void pdf_include_chars(void) +{@+str_number s; + pool_pointer k; /*running indices*/ + internal_font_number f; + + scan_font_ident(); + f=cur_val; + if (f==null_font) + pdf_error("font","invalid font identifier"); + scan_pdf_ext_toks(); +} + +@ @<Implement \.{\\pdfliteral}@>= +{@+ int k; + if (scan_keyword("shipout")) k=pdf_lateliteral_node; + else k=pdf_literal_node; + if (scan_keyword("direct")) do_nothing; + else if (scan_keyword("page")) do_nothing; + else do_nothing; + if (k==pdf_literal_node) scan_pdf_ext_toks(); + else scan_pdf_ext_late_toks(); +} + +@ + +@d colorstack_set 0 +@d colorstack_push 1 +@d colorstack_data 1 /* last value where data field is set */ +@d colorstack_pop 2 +@d colorstack_current 3 + +@<Implement \.{\\pdfcolorstack}@>= +{@+int i; + scan_int(); + if (scan_keyword("set")) {@+ + i=colorstack_set; + } + else if (scan_keyword("push")) {@+ + i=colorstack_push; + } + else if (scan_keyword("pop")) {@+ + i=colorstack_pop; + } + else if (scan_keyword("current")) {@+ + i=colorstack_current; + } + else{@+ + i=-1; /*error*/ + } + if (i >= 0) {@+ + if (i <= colorstack_data) {@+ + scan_pdf_ext_toks(); + } + } +} + +@ @<Implement \.{\\pdfobj}@>= +{@+ + if (scan_keyword("reserveobjnum")) {@+ + @<Scan an optional space@>; + } + else{@+ + if (scan_keyword("useobjnum")) scan_int(); + if (scan_keyword("stream")) {@+ + if (scan_keyword("attr")) scan_pdf_ext_toks(); + } + if (scan_keyword("file")) do_nothing; + scan_pdf_ext_toks(); + } +} + +@ @<Implement \.{\\pdfoutline}@>= +{@+ + if (scan_keyword("attr")) scan_pdf_ext_toks(); + scan_action(); + if (scan_keyword("count")) scan_int(); + scan_pdf_ext_toks(); +} + +@ @<Implement \.{\\pdfxform}@>= +{@+ + if (scan_keyword("attr")) scan_pdf_ext_toks(); + if (scan_keyword("resources")) scan_pdf_ext_toks(); + scan_register_num(); +} + +@ The next procedure is neede for \.{\\pdfthread} and \.{\\pdfstartthread} + +@<Declare procedures needed in |do_ext...@>= +static void scan_thread_id(void) +{@+if (scan_keyword("num")) scan_int(); + else if (scan_keyword("name")) scan_pdf_ext_toks(); +} + +@ The next functions are used to implement \.{\\pdfximage}. +@<Declare procedures needed in |do_ext...@>= +static void scan_pdf_box_spec(void) /*scans PDF pagebox specification*/ +{@+ + if (scan_keyword("mediabox")) return; + else if (scan_keyword("cropbox")) return; + else if (scan_keyword("bleedbox")) return; + else if (scan_keyword("trimbox")) return; + else if (scan_keyword("artbox")) return; +} + +static void scan_image(void) +{@+ scan_alt_rule(); + if (scan_keyword("attr")) scan_pdf_ext_toks(); + if (scan_keyword("named")) scan_pdf_ext_toks(); + else if (scan_keyword("page")) scan_int(); + if (scan_keyword("colorspace")) scan_int(); + scan_pdf_box_spec(); + scan_pdf_ext_toks(); +} + + +@ The current version of the {\tt -pdf} version does not implementa all \pdfTeX\ primitives. + For example, the primitves \.{\\pdfcopyfont} and \.{\\letterspacefont} are not defined but + dummy versions could be implemented with |scan_font_ident| and |scan_int|. + + +@* Index. +Here is where you can find all uses of each identifier in the program, +with underlined entries pointing to where the identifier was defined. +If the identifier is only one letter long, however, you get to see only +the underlined entries. {\sl All references are to section numbers instead of +page numbers.} + +This index also lists error messages and other aspects of the program +that you might want to look up some day. For example, the entry +for ``system dependencies'' lists all sections that should receive +special attention from people who are installing \TeX\ in a new +operating environment. A list of various things that can't happen appears +under ``this can't happen''. Approximately 40 sections are listed under +``inner loop''; these account for about 60\pct! of \TeX's running time, +exclusive of input and output. diff --git a/Build/source/texk/web2c/texprofdir/texprofile.w b/Build/source/texk/web2c/texprofdir/texprofile.w new file mode 100644 index 00000000000..42ef5ed324c --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/texprofile.w @@ -0,0 +1,1981 @@ +% This file is part of HINT +% Copyright 2017-2024 Martin Ruckert, +% Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +% OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +% THE SOFTWARE. +% +% Except as contained in this notice, the name of the copyright holders shall +% not be used in advertising or otherwise to promote the sale, use or other +% dealings in this Software without prior written authorization from the +% copyright holders. + +\input texprofilemac.tex + +%% defining how to display certain C identifiers + +@s int8_t int +@s uint8_t int +@s int16_t int +@s uint16_t int +@s uint32_t int +@s int32_t int +@s uint64_t int +@s bool int + +@ + +\makeindex +\maketoc +\makecode +%\makefigindex +\titletrue + +\null + +\font\largetitlefont=cmssbx10 scaled\magstep4 +\font\Largetitlefont=cmssbx10 at 40pt +\font\hugetitlefont=cmssbx10 at 48pt +\font\smalltitlefontit=cmbxti10 scaled\magstep3 +\font\smalltitlefont=cmssbx10 scaled\magstep3 + +%halftitle +\def\raggedleft{\leftskip=0pt plus 5em\parfillskip=0pt +\spaceskip=.3333em \xspaceskip=0.5em \emergencystretch=1em\relax +\hyphenpenalty=1000\exhyphenpenalty=1000\pretolerance=10000\linepenalty=5000 +} +\hbox{} +\vskip 0pt plus 1fill +{ \baselineskip=60pt + \hugetitlefont\hfill \TeX profile:\par + \Largetitlefont\raggedleft Profiling \TeX\ Source Files\par +} +\vskip 0pt plus 5fill +\eject +% verso of half title +\titletrue +\null +\vfill +\eject + +% title +\titletrue +\hbox{} +\vskip 0pt plus 1fill +{ + \baselineskip=1cm\parindent=0pt + {\largetitlefont\raggedright {\TeX}profile}\par + \leftline{\smalltitlefont Version 1.0} + \vskip 10pt plus 0.5fill + \leftline{\smalltitlefont Profiling \TeX} + \vskip-3pt + \leftline{\smalltitlefont Source Files} + \vskip 10pt plus 0.5fill + \hskip 0pt plus 2fill{\it F\"ur Beatriz}\hskip 0pt plus 0.5fill\hbox{} + \bigskip + \vskip 10pt plus 3fill + \raggedright\baselineskip=12pt + {\bf MARTIN RUCKERT} \ {\it Munich University of Applied Sciences}\par + \bigskip + \leftline{First edition} + \bigskip +% \leftline{\bf Eigendruck im Selbstverlag} +% \bigskip +} +\eject + +% verso of title +% copyright page (ii) +\titletrue +\begingroup +\figrm +\parindent=0pt +%\null +{\raggedright\advance\rightskip 3.5pc +The author has taken care in the preparation of this book, +but makes no expressed or implied warranty of any kind and assumes no +responsibility for errors or omissions. No liability is assumed for +incidental or consequential damages in connection with or arising out +of the use of the information or programs contained herein. + +\bigskip +{\figtt\obeylines\obeyspaces\baselineskip=11pt +Ruckert, Martin. + Profiling \TeX\ Source Files + Includes index. +% ISBN XXX-XXXXXXX-X +} +\bigskip + +{\raggedright\advance\rightskip 3.5pc plus 35pt +\def\:{\discretionary{}{}{}} +The internet page {\tt http:\://hint.\:userweb.\:mwn.\:de/} +may contain current information about this book, downloadable software, +and news. + +\vfill +Copyright $\copyright$ 2024 by Martin Ruckert +\smallskip +All rights reserved. +Printed by Kindle Direct Publishing. +This publication is protected by copyright, and permission must be +obtained prior to any prohibited reproduction, storage in +a~retrieval system, or transmission in any form or by any means, electronic, +mechanical, photocopying, recording, or likewise. +To obtain permission to use material from this work, please submit a written +request to Martin Ruckert, +Hochschule M\"unchen, +Fakult\"at f\"ur Informatik und Mathematik, +Lothstrasse 64, +80335 M\"unchen, +Germany. +\medskip +{\tt martin.ruckert\:@@hm.edu} +\medskip +%ISBN-13: 979-854992684-4\par +\medskip +%First printing: August 2019\par +\medskip +%Last commit: \input lastcommit.tex +\par +} +} +\endgroup + + +\frontmatter + + + +\plainsection{Preface} +This program is a result not of need or necessity but of curiosity. +Being used to have a profiler at my disposal whenever I write a program, +I just wanted to know if a profiler for \TeX\ could be written and what it would +tell me. +So when I was considering what could be my contribution to the TUG 2024 conference +in Prague, I thought ``How about writing a profiler for \TeX?'' +So I started the project and completed it just in time for the conference. + +The following document describes how to use {\tt texprofile}---the program that +displays the data gathered by {\tt texprof}---in a readable form. + +In a future version, this document will also contain a ``User Manual'' +that contains just the information needed by the person who wants to +find out how \TeX\ is spending its time when processing a document. +Until then be brave and try to cope with all the extra information +contained in the following document! + +\vskip 1cm +\noindent {\it M\"unchen\hfil\break +May 28, 2024 \hfill Martin Ruckert} + + +\tableofcontent +%\thefigindex + + +\mainmatter +\section{The |main| Program} + +The program presented here reads data collected by a run of {\tt texprof} +and presents it in a readable way. +The structure of the program is similar to a lot of \CEE\ programs: + +@p #include <stdlib.h> +#include <stdio.h> +#include <stdint.h> +#include <stdbool.h> +#include <ctype.h> +#include <string.h> +@<declarations@>@; +@<error handling@>@; +@<functions@>@; + +int main(int argc, char *argv[]) +{ int i, k; + @<process the command line@>@; + @<read the input file@>@; + @<write the output file@>@; + return 0; +} +@ + +\section{Reading the Input File} + +Let us start with the interesting stuff and read the input file. +@<declarations@>= +static char *input_file_name=NULL; +static FILE *in=NULL; +@ + +First we need to open the input file. If that is successful, we check +that there is a ``file marker'': a special sequence of characters that we expect +at the very beginning of the input file and at various other places in the file just +to make sure we have a file that is really meant to be read by {\tt texprofile}. + +@d FILE_MARKER "TEX PROF" + +@<read the input file@>= +if (input_file_name==NULL) + commandline_error("no input file given"); +in = fopen(input_file_name,"rb"); +if (in==NULL) +{ char *tmp=malloc(strlen(input_file_name)+7); + if (tmp!=NULL) + { strcpy(tmp,input_file_name); + strcat(tmp,".tprof"); + input_file_name=tmp; + in = fopen(input_file_name,"rb"); + } +} +if (in==NULL) + error("Unable to open input file"); +check_file_marker("The input file does not seem to contain TeX profile data"); +@ + +After the initial file marker we read the whole file which is +organized in four sections separated by file markers. + +@<read the input file@>= + @<read size data@>@; + check_file_marker("Invalid size data"); + @<read file names@>@; + check_file_marker("Invalid file names"); + @<read macro names@>@; + check_file_marker("Invalid macro names"); + @<read timing data@>@>@; + check_file_marker("Invalid timing data"); + fclose(in); +@ + +Before we explain how to read the different sections, we define functions +to read multi-byte numbers in big endian format. + +@<functions@>= +static uint8_t fget1(void) +{ unsigned char buf[1]; + if (fread(buf,1,1,in)!=1) + error("Unexpected end of input"); + return (uint8_t)buf[0]; +} + +static uint16_t fget2(void) +{ unsigned char buf[2]; + if (fread(buf,1,2,in)!=2) + error("Unexpected end of input"); + return ((uint16_t)buf[0]<<8)+buf[1]; +} + +static uint32_t fget4(void) +{ unsigned char buf[4]; + if (fread(buf,1,4,in)!=4) + error("Unexpected end of input"); + return ((((((uint32_t)buf[0]<<8)+buf[1])<<8)+buf[2])<<8)+buf[3]; +} +@ + +\subsection{Reading the Size Data} +Here are the variables for the size data and the code to read them. + +@<declarations@>= +static unsigned int file_num, /*number of input files*/ +file_name_num, /*number of byte needed for the file names*/ +macro_num, /*number of macros defined*/ +macro_name_num, /*number of byte needed for the macro names*/ +stamp_num, /*number of time stamps*/ +depth_num; /*maximum stack depth*/ +@ + +@<read size data@>= +file_num=fget2(); +file_name_num=fget2(); +stamp_num=fget4(); +if (stamp_num==0) + error("The input file contains no time samples"); +macro_num=fget2(); +macro_name_num=fget4(); +depth_num=fget2(); +@ + +We conclude the reading of size data by using these values to allocate +properly sized arrays. + +@<read size data@>= + @<allocate arrays@>@; +@ + + +\subsection{Reading the File Names} +The first section in the input file contains the full names of all +\TeX input files processed by {\tt texprof}. +We need arrays of pointers to the full file names and +to the short file names and a character array to store the actual file names. + +@<declarations@>= +static char **full_file_names, **file_names, *file_name_buffer; +#define ALLOCATE(V,S) V = calloc(S,sizeof(*V));\ + if (V==NULL)@+ error("Out of memory for " #V "[" #S "]"); +@ + +@<allocate arrays@>= +ALLOCATE(full_file_names,file_num); +ALLOCATE(file_names,file_num); +ALLOCATE(file_name_buffer,file_name_num); +@ + + +The first three files are not ordinary files but are used for special +purposes: File number 0 is used for tokens that {\tt texprof} can not +associate with an input file. These are usually only the tokens that +are loaded from a \TeX\ format files since format files produced by \TeX's +\.{\\dump} primitive do not contain file and line information. +File number 1 is used for the ``system'' file. Its ``line numbers'' identify +not a ``line'' but the kind of action that was taken by the system. +File number 2 is used for the ``terminal'' file. It is used for +text that is entered either on the command line or in an interactive run of \TeX. + +@d unknown_file 0 /*unknown origin*/ +@d system_file 1 /*generated by the system*/ +@d terminal_file 2 /*read from the terminal*/ +@d DIR_SEPARATOR '/' + +@<read file names@>= +i=k=0; +while (k< file_num) +{ char c; + full_file_names[k]=file_names[k]=file_name_buffer+i; + do { c = fget1(); + if (i>=file_name_num) error("File name buffer overflow"); + file_name_buffer[i++]=c; + if (c==DIR_SEPARATOR) file_names[k]=file_name_buffer+i; + } while (c!=0); + k++; +} +@ + + +\subsection{Reading the Macro Names} +After the file names, we read the macro names and construct the +|macro_map| table. It maps the multi-letter macro numbers from \TeX's +hash table to the sequential numbering used to access the +|macro_names| array. This map is used when reading the time stamps to +map \TeX's control sequence numbers to the sequential numbers needed +to access the macro names. After reading the time stamps, the +|macro_map| array can be deallocated. + +@<declarations@>= +static char *macro_name_buffer, **macro_names; +static uint16_t *macro_stack; +static struct map +{ uint16_t n; /* name */ + uint16_t m; /*macro definition*/ +} *macro_map; +@ + +@<allocate arrays@>= +ALLOCATE(macro_names,macro_num); +ALLOCATE(macro_name_buffer,macro_name_num); +ALLOCATE(macro_map,undefined_control_sequence+every_eof_text+1); +ALLOCATE(macro_stack,depth_num+1); +@ + +@<read macro names@>= +i=0;k=0; +while (k< macro_num) +{ int j; + char c; + j=fget2(); + macro_map[j].n=k; + macro_map[j].m=0; + macro_names[k++]=macro_name_buffer+i; + do { c=fget1(); + if (i>=macro_name_num) error("Name buffer overflow"); + macro_name_buffer[i++]=c; + } while (c!=0); +} +for (i=1;i<=every_eof_text;i++) +{ macro_map[undefined_control_sequence+i].n=macro_num+i; + macro_map[undefined_control_sequence+i].m=0; +} + +@ + +To print a macro name, we use the function |print_cs|. +Given a control sequence number |n|, +it can be either from an active character, or from a +single letter control sequence or from a multi-letter +control sequence. +If the number |n| comes from an active character, +we get the character by subtracting |active_base|; +If it comes comes from a single character +control sequence, we get the character by subtracting +|single_base|; if |n|is equal to |null_cs|, it comes +from the empty control sequence; and if it comes +from a multi-letter control sequence, |n| is already +mapped to the macro name index plus |hash_base|. + +@d active_base 1 /*beginning of region 1, for active character equivalents*/ +@d single_base (active_base+256) /*equivalents of one-character control sequences*/ +@d null_cs (single_base+256) /*equivalent of \.{\\csname\\endcsname}*/ +@d hash_base (null_cs+1) /*beginning of region 2, for the hash table*/ +@d hash_size 45000 /*maximum number of control sequences; it should be at most + about |(mem_max-mem_min)/(double)10|*/ +@d frozen_control_sequence (hash_base+hash_size) /*for error recovery*/ +@d frozen_null_font (frozen_control_sequence+11) /*permanent `\.{\\nullfont}'*/ +@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/ +@d output_text 6 +@d every_eof_text 15 + +@<functions@>= +void print_cs(int n) +{ if (n< hash_base) + { if (n < single_base) + { int c=n-active_base; + if (c<0) printf("File"); + else if (isprint(c)) printf("%c",c); else printf("0x%02x",c); + } + else if (n < null_cs) + { int c=n-single_base; + if (isprint(c)) printf("\\%c",c); else printf("\\0x%02x",c); + } + else printf("Empty"); + } + else if (n>=macro_num+hash_base) + printf("\\%s",every_name[n-hash_base-macro_num-output_text]); + else + printf("\\%s",macro_names[n-hash_base]); +} +@ + + +\subsection{Reading the Time Stamps} +The last section contains the time stamps collected during the run of +{\tt texprof}. The time stamps are stored for later processing in the +|stamps| array. In addition to the timing data, the time stamps +section contains information about macro calls. + +@<declarations@>= +struct stamp { +uint8_t c; /*command*/ +uint8_t f; /*file*/ +uint16_t l; /*line*/ +uint16_t d; /*depth*/ +uint16_t m; /*macro*/ +uint32_t t; /*time*/ +} *stamps; + +struct macro_def { +uint16_t n; /* name */ +uint8_t f; /*file*/ +uint16_t l; /*line*/ +uint16_t link; /* link to another |macro_def|*/ +int a; /* number of concurrent activation records */ +int count; /* number of calls */ +uint16_t e; /* list of outgoing edges */ +uint64_t t; /* time used for this macro */ +uint64_t T; /* cumulative time spent in this macro */ +uint64_t s; /* start time of first activation */ +} *macro_defs; + +int macro_def_num; /*macros allocated*/ +int macro_def_count; /*macros used*/ +@ + +Since the |macro_defs| array will contain only those macros that +actually occur in the timing data, it is hard to predict the size +of the |macro_defs| array. So either a first pass over all the timing +data or a dynamic array is needed. Here the decision was to use +a dynamic array. + +@<allocate arrays@>= +ALLOCATE(stamps,stamp_num); +macro_def_num=16; +macro_def_count=1; /* zero element is a sentinel */ +ALLOCATE(macro_defs,macro_def_num); +@ + +@<functions@>= +#define REALLOC(A,C,N) \ + { N=N*1.4142136+0.5; /* $\sqrt 2$ */\ + A=realloc(A,N*sizeof(*A));\ + if (A==NULL) error("Out of memory"); \ + memset(A+C,0,(N-C)*sizeof(*A)); \ + } + +int new_macro_def(void) +{ if (macro_def_count<macro_def_num) + return macro_def_count++; + REALLOC(macro_defs,macro_def_count,macro_def_num); + return macro_def_count++; +} +@ + +When reading the timing data, we will allocate for each new macro +call a new macro definition with its name and the file and line of +its definition. One problem to overcome here is that \TeX\ allocates +a macro name in the hash table only once for every name even if that +name is reused for various macros in different files or in different lines. +Here we need a unique number for each of these macros. +For simplification, however, we will use the same number if two +macros with the same name are defined in the same file and the same +line. If you need to distinguish these macros, move the definitions +to separate lines (or use different names because it will not only +confuse the profiler but also the readers of your program). + +To convert the file, line, and control sequence number to +a unique macro definition number, we use the |macro_map| array +to store the macro definition number and link multiple macro +definitions that share the same name into a linked list +using the |link| field of the macro definition. + +@<functions@>= +int get_macro_def(int f, int l, int c) +{ int m = macro_map[c].m; + if (m==0) + { m=new_macro_def(); + macro_map[c].m=m; + } + else + { loop: + if (macro_defs[m].f==f && macro_defs[m].l==l) + return m; + else if (macro_defs[m].link==0) + { int n=new_macro_def(); + macro_defs[m].link=n; + m= n; + } + else + { m= macro_defs[m].link; + goto loop; + } + } + if (c<hash_base) + macro_defs[m].n=c; + else + macro_defs[m].n=hash_base+macro_map[c].n; + macro_defs[m].f=f; + macro_defs[m].l=l; + macro_defs[m].link=0; + return m; +} +@ + +To print a macro name we use the function |print_macro|. Because the name +will not uniquely identify a macro---\TeX\ allows using the same name for +different macros---there is an option to print the file and line number in +square brackets after the macro name. + +@<declarations@>= +bool opt_macro_id=false; +@ + +@<process options@>= +case 'i': opt_macro_id=true;break; +@ + +@<explain format options@>= +"-i add the macros file and line numbers after the macro name\n"@; +@ + +@<functions@>= +void print_macro(int i) +{ print_cs(macro_defs[i].n); + if (opt_macro_id) printf(" [%d,%d]",macro_defs[i].f,macro_defs[i].l); +} +@ + +Now we can read the time stamps and the changes to the macro stack. +Each element of the timing data starts with a one byte command value +which is followed by a variable number of data bytes. + +After the |system_macro_pop| command follows a two byte +new stack depth. +After all other commands follows a one byte file number and a two byte +line number. The |system_macro_push| command is then followed +by a two byte new stack depth and a two byte control sequence number. +All remaining commands are followed by a four byte elapsed +time in nano seconds. + +@<declarations@>= +uint64_t total_time; /*sum of all command times*/ +uint32_t total_num; /*number of all command times*/ +uint64_t *file_time, *cmd_time; +int *file_line; /* maximum line number/offsets per file */ +int *cmd_freq; +int cur_depth; +@ + +@ + +@d POP_BIT 0x80 + +@<read timing data@>= +ALLOCATE(file_time,file_num); +ALLOCATE(cmd_time,cmd_num); +ALLOCATE(file_line,file_num+1); +ALLOCATE(cmd_freq,cmd_num); + + +cur_depth=-1; +total_time=0; +total_num=0; +macro_defs[0].a=1; +i=0; +{ fget1(); /* the initial call */ + @<read a macro call@>@; + @<time a push command@>@; +} +while (i<stamp_num) +{ uint8_t c=fget1(); + switch (c) + { case system_macro_pop_0: break; + case system_macro_pop_0+1: + case system_macro_pop_0+2: + case system_macro_pop_0+3: + case system_macro_pop_0+4: + case system_macro_pop_0+5: + case system_macro_pop_0+6: + case system_macro_pop_0+7: + case system_macro_pop_0+8: + case system_macro_pop_0+9: + case system_macro_pop_0+10: + { int d=c-system_macro_pop_0; + @<store pop |d|@>@; + break; + } + case system_macro_pop_small: + { int d=fget1(); + @<store pop |d|@>@; + break; + } + case system_macro_pop: + { int d=fget2(); + @<store pop |d|@>@; + break; + } + case system_macro_push: + { @<read a macro call@>@; + cur_depth++; + macro_stack[cur_depth]=m; + macro_defs[m].count++; + macro_defs[m].a++; + if (macro_defs[m].a==1) + { macro_defs[m].s=total_time; + } + start_child(macro_stack[cur_depth-1],m); + } + break; +default: + if (c&POP_BIT) + { int d=1; + @<store pop |d|@>@; + c=c&~POP_BIT; + } + { uint8_t f=fget1(); + uint16_t l=fget2(); + uint32_t t=fget4(); + uint16_t m=macro_stack[cur_depth]; + if (f>=file_num) error("File number out of range"); + if (c>=cmd_num) error("Command number out of range"); + stamps[i].c=c; + stamps[i].f=f; + if (l> file_line[f]) + file_line[f]=l; + stamps[i].l=l; + stamps[i].t=t; + total_time+=t; + file_time[f]+=t; + macro_defs[m].t+=t; + cmd_time[c]+=t; + cmd_freq[c]++; + total_num++; + stamps[i].d=cur_depth; + stamps[i].m=m; + } + i++; +#ifdef DEBUG + printf("<%d:%d:%d> %s\n",i-1,c, cur_depth, cmd_name[c]); +#endif + break; + } +} +macro_defs[macro_stack[0]].T=total_time; +macro_defs[0].count=0; +macro_defs[0].T=0; +macro_defs[0].f=0; +macro_defs[0].l=0; + + +#ifdef DEBUG + printf("Finished reading %d commands, depth=%d\n",i,cur_depth); +#endif +@ + +The table of |cmd_name|s is defined in the appendix. + +@<store pop |d|@>= + stamps[i].c=system_macro_pop; + stamps[i].d=cur_depth-d; +#ifdef DEBUG + printf("{%d:%d:%d>%d}\n",i,system_macro_pop,cur_depth+d,cur_depth); +#endif + @<time a pop command@>@; + i++; +@ + + +@<read a macro call@>= + int f,l,n,m; + f=fget1(); + l=fget2(); + n=fget2(); + m=get_macro_def(f, l, n); + stamps[i].c=system_macro_push; + stamps[i].f=f; + stamps[i].l=l; + stamps[i].d=cur_depth; + stamps[i].m=m; +#ifdef DEBUG + printf("[%d:%d<%d:",i,cur_depth, cur_depth+1); + print_macro(m); + printf("]\n"); +#endif + i++; +@ + +\section{Writing the Output} +For simplicity and because it is often sufficient to have a +short look at the results, we send the output to the standard output stream. +Various tables of output can be selected using options on the command line. +For the selection of tables, we use uppercase letters like ``{\tt R}''; to modify the +presentation of the tables, we use lower case letters like ``{\tt s}'' or ``{\tt n}''. + +\subsection{The Raw Time Stamps} +Let us start with some simple options: ``{\tt -R}'' will output +the raw time stamps. This is useful if the profiler runs for +a very short time or if a separate program wants to analyze the data. +In the long list of time stamps the file names +would only be distracting. So we print only the file numbers and +show the file names in a separate table (see below). +The option ``-s'' will show information about the macro stack along +with the time stamps and the option ``-n'' will show the serial numbers, +a feature that is mainly needed for debugging the {\tt texprofile} program. + +@<declarations@>= +bool opt_raw=false; +bool opt_raw_stack=false; +bool opt_raw_num=false; +@ + +@<process options@>= +case 'R': opt_raw=true; break; +case 's': opt_raw_stack=true; break; +case 'n': opt_raw_num=true; break; +@ + +@<explain table options@>= +"-R show the table of raw time stamps\n"@; +@ @<explain format options@>= +"-s show the changes of the macro stack\n"@; +"-n show the time stamp numbers\n"@; +@ + +The following table shows the time in nano seconds, +the file number, the line number and the command executed. +Here and in the following tables, all fields are separated +by a TAB characters. So you can import the data in common +office products for further processing (see also the option {\tt -m} below). + +@<show all time stamps if requested@>= +if (opt_raw) +{ Mprintf("\nAll time stamps in order of appearance:\n"); + if (opt_raw_num) Mprintf("number\t"); + Mprintf("file\t line\ttime/ns\tcommand\t level\tmacro\n"); + for (i=1;i<stamp_num;i++) + { int m=stamps[i].m; + @<extract |t|, |f|, |l|, and |c| from stamp |i|@>@; + if (c <= system_profile_off) + { if (opt_raw_num) printf("%6d\t",i); + print_fl(f,l); + printf("\t%7u\t%-23s\t%5d\t",t,CMD_NAME(c),stamps[i].d); + print_macro(m); + printf("\n"); + } + else if (opt_raw_stack) + { if (opt_raw_num) printf("%6d\t",i); + printf("\t\t\t%-23s\t%5d\t",CMD_NAME(c),stamps[i].d); + if (c==system_macro_push) + print_macro(m); + printf("\n"); + } + } +} +@ + + + + + +@<extract |t|, |f|, |l|, and |c| from stamp |i|@>= +uint32_t t=stamps[i].t; +uint16_t l=stamps[i].l; +uint8_t f=stamps[i].f; +uint8_t c=stamps[i].c; +@ + +\subsection{The Raw Macro Stack Changes} +Further we can display the raw macro stack changes. +@<declarations@>= +bool opt_stack=false; +@ + +@<process options@>= +case 'S': opt_stack=true; break; +@ + +@<explain table options@>= +"-S show the table of macro stack changes\n"@; +@ + +@<show the complete macro stack if requested@>= +if (opt_stack) +{ int c,d,m; + Mprintf("\nThe macro stack and its nesting:\n" + "command\tdepth\tname [id]\n"); + for(i=0;i<stamp_num;i++) + { c=stamps[i].c; + if (c<system_macro_push) + continue; + d=stamps[i].d; + m=stamps[i].m; + printf("%5d\t%s\t",d,CMD_NAME(c)); + if (c==system_macro_push) + print_macro(m); + printf("\n"); + } +} +@ + + + + +\subsection{The Summary} +Now we can print print the totals for the \TeX\ run. Note that the option to +show the summary is on by default, but can be switched off with the +``{\tt -N}'' command line switch. + +@<declarations@>= +bool opt_summary=true; +@ + +@<process options@>= +case 'N': opt_summary=false;break; +@ + +@<explain table options@>= +"-N do not show the table of global information\n"@; +@ + + +@<show the total time if requested@>= +if (opt_summary) +{ printf("\n" + "Total time measured: %s\n", time_str(total_time)); + printf("Total number of samples: %6d\n",total_num); + if (total_num>0) + printf("Average time per sample: %s\n",time_str(total_time/total_num)); + printf("Total number of files: %5d\n",file_num); + printf("Total number of macros: %5d\n",macro_num); + printf("Maximum stack nesting depth: %5d\n",depth_num); +} +@ + +To print times over a wide range in a useful format, +we use the following function. The option ``{\tt -m}'' can be used +to display plain nano seconds which is the preferable if you plan +to import the data into another program for further processing. +The macro |Mprintf| is used instead of |printf| for lines that +should not be printed when |opt_machine| is true. + +@<declarations@>= +bool opt_machine=false; +#define Mprintf(...) opt_machine?(void)0:printf(__VA_ARGS__) +@ + +@<process options@>= +case 'm': opt_machine=true;opt_summary=false;break; +@ + +@<explain format options@>= +"-m optimize output for machine readability\n"@; +@ + + +@<functions@>= +char * time_str(double t) +{ static char str[20]; + if (opt_machine) + snprintf(str,20,"%12ld",(long)t); + else + { if (t<1000.0) + snprintf(str,20," %7.2f ns",t); + else if (t<1000000.0) + snprintf(str,20," %7.2f us",t/1000.0); + else if (t<1000000000.0) + snprintf(str,20," %7.2f ms",t/1000000.0); + else + snprintf(str,20," %7.2f s",t/1000000000.0); + } + return str; +} +@ + +We continue with a function to print file and line information in a readable way. + + +@<functions@>= +void print_fl(int f, int l) +{ if (f==system_file && !opt_machine) + printf("system\t%7s",line_name[l]); + else + printf("%4d\t%5d",f,l); +} +@ + + + + +\subsection{The File Summary} + +@<declarations@>= +bool opt_files=false; +@ + +@<process options@>= +case 'F': opt_files=true; break; +@ + +@<explain table options@>= +"-F show all files\n" +@ + + +Next we can print a summary table for the files: + +@<show the file summary if requested@>= +if (opt_files) +{ Mprintf("\nFiles in the order of appearance:\n"); + Mprintf(" file\t lines\tpercent\t time" + "\tname\n"); + for (i=0; i<file_num; i++) + { double p=(100.0*file_time[i])/total_time; + if (p>=percent_limit) + printf("%6d\t%6d\t%6.2f%%\t%s\t%s\n", + i, file_line[i], + p, time_str(file_time[i]), + full_file_names[i]); + } +} +@ + +\subsection{The Command Summary} +And similar for commands: + +@<declarations@>= +bool opt_cmd=false; +@ + +@<process options@>= +case 'C': opt_cmd=true;break; +@ + +@<explain table options@>= +"-C show table of command summaries\n"@; +@ + + +@<show the command summary if requested@>= +if (opt_cmd) +{ int *cmd_link=NULL; + @<sort the commands by time@>@; + Mprintf("\nCommand summary:\n"); + Mprintf(" cmd\t time\tpercent\t count\t average\tname\n"); + for (i=cmd_link[cmd_num]; i>=0; i=cmd_link[i]) + if (cmd_freq[i]>0) + { printf("%4d\t%s\t%6.2f%%", + i, time_str(cmd_time[i]), + (100.0*cmd_time[i])/total_time); + printf("\t%6d\t %s\t%s\n", + cmd_freq[i],time_str(cmd_time[i]/cmd_freq[i]), + CMD_NAME(i)); + } + free(cmd_link); +} +@ + +To sort the commands in decreasing order of times, +we allocate an array of links and sort the linked +list using a simple insertion sort. +We keep the links in |cmd_link| and the list head in +|cmd_link[cmd_num]|. The end of the link is marked +by $-1$. + +@<sort the commands by time@>= +ALLOCATE(cmd_link,cmd_num+1); +cmd_link[cmd_num]=-1; +for (i=0; i<cmd_num; i++) +{ int l=cmd_num; + uint32_t t=cmd_time[i]; + while (cmd_link[l]>=0 && cmd_time[cmd_link[l]]>t) + l=cmd_link[l]; + cmd_link[i]=cmd_link[l]; + cmd_link[l]=i; +} +@ + +\subsection{The Macro Summary} +Here is a table of all macros called. It should be possible to sort +this table by either the direct time or be the cumulative +time. Further there should be an option to switch between absolute +times and relative times as a percentage of the total time. + +@<declarations@>= +bool opt_macro=false; +@ + +@<process options@>= +case 'M': opt_macro=true;break; +@ + +@<explain table options@>= +"-M show the table of all macros called\n"@; +@ + +To compute the macro summary, we traverse the time stamps while reading. + +If there is a push command we do this: +@<time a push command@>= + cur_depth++; + macro_stack[cur_depth]=m; + macro_defs[m].count++; + macro_defs[m].a++; + if (macro_defs[m].a==1) + { macro_defs[m].s=total_time; + } +@ + +If there is a pop command we do this: + +@<time a pop command@>= +while (cur_depth>stamps[i].d) +{ int m=macro_stack[cur_depth]; + if (macro_defs[m].a==1) + macro_defs[m].T+=total_time-macro_defs[m].s; + end_child(macro_stack[cur_depth-1],m); + macro_defs[m].a--; + cur_depth--; +} + +@ + +@<show the table of macros profiled if requested@>= +if (opt_macro) +{ sort_macros(); + Mprintf("\nMacros profiled:\n"); + Mprintf(" file\t line\tcalls\t time direct\t cumulative\tname\n"); + i=0; + do + { printf("%5d\t%5d\t",macro_defs[i].f,macro_defs[i].l); + printf("%5d\t%s",macro_defs[i].count, time_str(macro_defs[i].t)); + printf("\t%s\t",time_str(macro_defs[i].T)); + print_macro(i); + printf("\n"); + i=macro_defs[i].link; + } while (i!=0); +} +@ + +To sort the edges by their cumulative time, we use a simple insertion sort. +We use |edges[0]| as the head of a new list and insert the list of edges +starting at |macro_defs[i].e|. While we do this we keep the estimates for +the cumulative time of the edges that we have to compute anyway. + +@<functions@>= +void sort_edges(uint16_t p) +{ int i,j,k; + edges[0].sibling=0; /*head node becomes the empty list*/ + i=macro_defs[p].e; + while (i!=0) + { uint64_t Ti=edges[i].T; + k=0; + while ((j=edges[k].sibling)!=0) + { uint64_t Tj=edges[j].T; + if (Ti<Tj) + k=j; + else + break; + } + edges[k].sibling=i; + k=i; + i=edges[i].sibling; + edges[k].sibling=j; + } + macro_defs[p].e=edges[0].sibling; +} +@ + +@ To sort the macros, we use again an insertion sort. So the procedure is +similar to the one we have just seen. +The value 0 is used as a sentinel +and |macro_defs[0].link| is used as the list head. + + +@<functions@>= +void sort_macros(void) +{ int i, k, j; + static bool sorted=false; + if (sorted) return; + macro_defs[0].link=0; + sort_edges(0); + for (i=1;i<macro_def_count;i++) + { uint64_t Ti=macro_defs[i].T; + k=0; + while ((j=macro_defs[k].link)!=0) + { uint64_t Tj=macro_defs[j].T; + if (Ti<Tj) + k=j; + else + break; + } + macro_defs[i].link=j; + macro_defs[k].link=i; + sort_edges(i); + } + sorted=true; +} +@ + + + +\subsection{Cumulative Information for Lines} +On the second pass over the time samples, we collect line based +information. To store this information, we allocate a big array +|total_line_time| containing the information for all lines and all +files. To access a specific line, we convert the |file_line| array +into an array of offsets for each file and add to it the line number. + +@<declarations@>= +int line_num=0; +@ + +@<turn |file_line| into an array of offsets + and compute the total number of lines@>= +line_num=0; +for (i=0;i<file_num;i++) +{ int fl= file_line[i]; + file_line[i]=line_num; + line_num=line_num+fl+1; +} +file_line[file_num]=line_num; +@ + +Now we are ready to compute the total time per line and the line +frequency. For the frequency counts, we count any consecutive sequence +of samples from the same file and line as one use of this line. So +using multiple macros from the same line without intervening code from +other lines will count only as a single access. + +We need the following arrays: +@<declarations@>= +static uint64_t *line_time=NULL; +static int *line_freq=NULL; +@ + +The following function allocates the dynamic arrays just declared and +fills them with content. Before doing so it checks that the arrays are +not yet allocated because this would imply that the function was +already called and there is nothing left to do. The information +collected here is required for both, the |opt_lines| and the +|opt_top_ten| tables, but there is no need to compute it twice. + +@<functions@>= +void collect_line_time(void) +{ int i, cur_f=-1,cur_l=-1; + if (line_time!=NULL) return; /*don't run a second time*/ + @<turn |file_line| into an array of offsets + and compute the total number of lines@>@; + ALLOCATE(line_time,line_num); + ALLOCATE(line_freq,line_num); + for (i=0; i<stamp_num; i++) + { @<extract |t|, |f|, |l|, and |c| from stamp |i|@>@; + if (c<=system_profile_off) + { line_time[file_line[f]+l]+=t; + if (cur_f!=f || cur_l!=l) + { line_freq[file_line[f]+l]++; + cur_f=f; cur_l=l; + } + } + } +} +@ + +Printing all lines with their time is usually not required. +We want only those lines that have a contribution that is above +a certain limit given as the percentage of total time. + +@<declarations@>= +double percent_limit=1.0; +bool opt_lines=false; +@ + +@<process options@>= +case 'L': opt_lines=true; break; +case 'p':{ char *endptr; + percent_limit=strtod(option+1, &endptr); + if (endptr==option+1) + commandline_error("-p<n> without a numeric argument <n>"); + else + option=endptr-1; + } + break; +@ + +@<explain table options@>= +"-L show the table of times per input line\n"@; +@ @<explain format options@>= +"-p<n> don't show information for items with cumulative time below <n> percent\n"@; +@ + + +@<show lines above limit if requested@>= +if (opt_lines) +{ uint64_t limit= total_time*percent_limit/100.0; + int k; + collect_line_time(); + Mprintf("\nLine summary:\n"); + if (percent_limit>0) + Mprintf("Only files and lines above %.2f%%:\n",percent_limit); + for (k=i=0; i<file_num; i++) + { if (file_time[i]<=limit) + k=file_line[i+1]; + else + { printf("%s\n",file_names[i]); + printf(" file\t line\tpercent\t time" + "\t count\t average\n"); + printf("%6d\t\t%6.2f%%\t%s\n", + i, (100.0*file_time[i])/total_time,time_str(file_time[i])); + + for (;k<file_line[i+1]; k++) + { uint64_t t=line_time[k]; + if (line_freq[k]>0 && t>=limit) + { if (i==system_file) + printf("\t%7s",line_name[k-file_line[i]]); + else + printf("\t%6d", k-file_line[i]); + printf("\t%6.2f%%\t%s", + 100.0*t/total_time,time_str(t)); + printf("\t%6d\t%s",line_freq[k],time_str(t/line_freq[k])); + printf("\n"); + } + } + printf("\n"); + } + } +} +@ + +\subsection{Finding the ``Top Ten'' Lines} +Now that we have for each line in one of the input files the total +time used by it, we can sort the lines by their time use. It is +neither efficient nor useful to sort all the lines, but it sufficient +to find the ``top ten''. + +@<declarations@>= +static uint64_t *tt_time; +static int *tt_file, *tt_line; +static int tt=10+1, tt_count;; +bool opt_top_ten; +@ + +@<process options@>= +case 'T': opt_top_ten=true;break; +case 't': { char *endptr; + tt=strtol(option+1, &endptr,10)+1; + if (endptr==option+1) + commandline_error("-t<n> without a numeric argument <n>"); + else if (tt<2 || tt>101) + commandline_error("-t<n> with <n> out of bounds"); + option=endptr-1; + } + break; +@ + +@<explain table options@>= +"-T show the table of the top 10 input lines\n"@; +@ @<explain format options@>= +"-t<n> replace 10 by n (2<=n<=100, default 10) for the top 10 input lines\n"@; +@ + + +@<find the top ten lines@>= +ALLOCATE(tt_time,tt); +ALLOCATE(tt_file,tt); +ALLOCATE(tt_line,tt); +tt_time[0]=0xFFFFFFFFFFFFFFFF; +tt_file[0]=0; +tt_line[0]=0; +tt_time[1]=0; +tt_file[1]=0; +tt_line[1]=0; +tt_count=2; +{ int f,l; + for (f=l=0; f< file_num; f++) + { for (;l<file_line[f+1]; l++) + { uint32_t t=line_time[l]; + if (t>tt_time[tt_count-1]) + @<insert |t| into the top ten list@>@; + } + } +} +@ + +Inserting a new time into the top ten array using insertion sort. + +@<insert |t| into the top ten list@>= +{ + if (tt_count<tt)tt_count++; + i= tt_count-1; + while (tt_time[i-1]<t) + { i--; + tt_time[i+1]=tt_time[i]; + tt_file[i+1]=tt_file[i]; + tt_line[i+1]=tt_line[i]; + } + tt_time[i]=t; + tt_file[i]=f; + tt_line[i]=l-file_line[f]; +} +@ + +@<show the top ten lines@>= +if (opt_top_ten) +{ collect_line_time(); + @<find the top ten lines@>@; + Mprintf("\nThe top ten lines:\n"); + Mprintf(" file\t line\tpercent\t absolute" + "\t count\t average\tfile\n"); + for (i=1; i< tt_count; i++) + { int freq=line_freq[tt_line[i]+file_line[tt_file[i]]]; + uint32_t t =tt_time[i]; + if (t>0 && freq>0) + { print_fl(tt_file[i],tt_line[i]); + printf("\t%6.2f%%\t%s", 100.0*t/total_time,time_str(t)); + printf("\t%6d\t%s\t%s\n",freq, time_str(t/freq), file_names[tt_file[i]]); + } + } +} +@ + + +\subsection{The Call Graph} +For each macro, we know already how often it was called, the +cumulative time, and the time directly spent in the macro. For the +call graph, we want to know for each macro which child macros it did +call, how often it did call each child, how much this child +contributed directly and cumulative to the macro. +From this data we could derive which parent macros +called it, how often it was called by each parent, and how much time +it contributed directly and cumulatively to the parent. +We need to form linked lists of edges, so +each edge has a link to its siblings. + +Following the conventions of {\tt gprof}, call counts are +displayed in the format $n/m$ where $n$ is the number of calls on the +edge of the call graph under consideration and $m$ is the total number +of calls. + +The table of macro definitions is already available, so what we need +is just a collection of the edges with counts, direct times and +cumulative times. It is possible to estimate direct and +cumulative times from the counts and the timing information we have +already for each macro, but this method is not very precise. + +Since macro calls can form recursive loops, the parent is not only +an ancestor of the child, the child may also be an ancestor of the parent +and its siblings. (Sounds horrible, but we are talking about macro calls.) +Therefore the cumulative time spent in executing a child macro +might include time spent in the parent macro or in its sibling macros. +Therefore we need to split the cumulative time spent in a macro into +two components: The cumulative time |T| that can be attributed to this macro alone, +and the time that is spent in this macro but is attributed to the +parent or a sibling. The call the later time the ``loop time'' |L|. +To compute |T| and |L|, we keep track of the activation count |a| +of each child, and keep two auxiliary variables |ts| and |Ts| when +we push the first activation record, and update |T| and |L| only +when we pop the last activation of the macro. +The variable |ts| is used to record the |total_time| +and |Ts| is used to record the sum of the time directly attributed to the parent +and the cumulative times attributed to all siblings. +Recomputing these values when we pop the final activation, +we can obtain the time |dt| passed since its first activation, +and the time |dT| attributed to parent and siblings since then. +We use |dT| to update |L| and |dt-dT| to update |T|. + +Here is the declaration of an |edge|: +@<declarations@>= +struct edge { +uint16_t child, sibling; +int count, a; +uint64_t T, L, ts, Ts; +} *edges; +int edges_count, edges_num; +@ + +The total number of outgoing and incoming edges can only be estimated. +So a dynamic allocation is used. + +@<allocate arrays@>= +edges_num=1024; +edges_count=1; /* zero element is a sentinel and a head */ +ALLOCATE(edges,edges_num); +@ + +@<functions@>= +uint16_t new_edge(void) +{ if (edges_count<edges_num) + return edges_count++; + REALLOC(edges,edges_count,edges_num); + return edges_count++; +} +@ + +When a parent macro with definition |p| calls +a child macro with definition |c|, the |start_child| function +is called after the new child macro is put on top of the macro stack. +We first search the existing edges of the parent for an existing +edge, possibly from a previous invocation, before we add a new edge. +Once the edge is found, we increment |count| and |a|. +If |a| changes from zero to one we update |T|, |L|, |ts|, and |Ts| +as described above. + +@<functions@>= +void start_child(int p, int c) +{ int e =macro_defs[p].e; + if (e==0) + { e =new_edge(); + macro_defs[p].e=e; + edges[e].child=c; + goto found; + } + do + { if (edges[e].child==c) + goto found; + else if (edges[e].sibling==0) + { int s=new_edge(); + edges[e].sibling=s; + e = s; + edges[e].child=c; + goto found; + } + else + e = edges[e].sibling; + } while (true); +found: + edges[e].a++; + edges[e].count++; + if (edges[e].a==1) + { uint16_t i; + edges[e].ts=total_time; + edges[e].Ts=macro_defs[p].t; + for (i= macro_defs[p].e;i!=0;i=edges[i].sibling) + if (i!=e) + edges[e].Ts +=edges[i].T; + } +} +@ + + +When we pop a child macro, we use the following function +to update |a| and in the case of |a==1| also |T| and |L|. + +@<functions@>= +void end_child(int p, int c) +{ uint16_t e = macro_defs[p].e; + while (e!=0) + { if (edges[e].child==c) + goto found; + else + e = edges[e].sibling; + } +found: + if (edges[e].a==1) + { uint16_t i; + uint64_t dt; + uint64_t dT; + dT=macro_defs[p].t; + for (i= macro_defs[p].e;i!=0;i=edges[i].sibling) + if (i!=e) + dT +=edges[i].T; + dt=total_time-edges[e].ts; + dT=dT-edges[e].Ts; + edges[e].L+=dT; + edges[e].T+=dt-dT; + } + edges[e].a--; +} +@ + +The information contained in the edges gives +a good view of the call graph. It can be shown using the ``{\tt -G}'' option. + +@ @<declarations@>= +bool opt_graph=false; +@ + +@<process options@>= +case 'G': opt_graph=true; break; +@ + +@<explain table options@>= +"-G show the table of the macro call graph\n"@; +@ + +@<show the macro call graph if requested@>= +if (opt_graph) +{ sort_macros(); + Mprintf("\nThe macro call graph:\n" + " time\t loop\tpercent\t count/total\tchild\n"); + i=macro_defs[0].link; + do + { int e; + uint64_t Ti=macro_defs[i].T; + if (100.0*Ti/total_time<percent_limit) + break; + print_macro(i); + printf("\n"); + printf("%s\t \t%6.2f%%\t", + time_str(Ti), 100.0*Ti/total_time); + if (opt_machine) + printf("\t%6d\t",macro_defs[i].count); + else + printf(" * \t"); + print_macro(i); + printf("\n"); + printf("%s\t \t", time_str(macro_defs[i].t)); + if (Ti!=0) + printf("%6.2f%%\t", 100.0*macro_defs[i].t/Ti); + else + printf("\t"); + if (opt_machine) + printf("%6d\t\t",macro_defs[i].count); + else + printf("%7d \t",macro_defs[i].count); + print_macro(i); + printf("\n"); + e =macro_defs[i].e; + while (e!=0) + { int c = edges[e].child; + int n =edges[e].count; + int m =macro_defs[c].count; + uint64_t Te=edges[e].T; + int64_t L=edges[e].L; + double p= 100.0*Te/Ti; + if (p>=percent_limit) + { printf("%s\t",time_str(Te)); + if (L==0 && !opt_machine) + printf(" \t"); + else + printf("%s\t", time_str(L)); + if (Ti!=0) + printf("%6.2f%%\t", p); + else + printf("\t"); + if (opt_machine) + printf("%6d\t%6d\t",n,m); + else + printf("%6d/%-6d\t",n,m); + print_macro(c); + printf("\n"); + } + e = edges[e].sibling; + } + printf("\n"); + i=macro_defs[i].link; + } while (i!=0); +} +@ + + +\subsection{All Tables} + +Now its time to bring all the different output tables together +in a useful sequence. + +@<process options@>= +case 'A': opt_files=opt_summary= + opt_cmd=opt_top_ten= + opt_graph=true; break; +@ + +@<explain table options@>= +"-A show all tables (equal to -TGFC) tables\n"@; +@ + +@<write the output file@>= +@<show all time stamps if requested@>@; +@<show the complete macro stack if requested@>@; +@<show the command summary if requested@>@; +@<show the file summary if requested@>@; +@<show lines above limit if requested@>@; +@<show the top ten lines@>@; +@<show the table of macros profiled if requested@>@; +@<show the macro call graph if requested@>@; +@<show the total time if requested@>@; +@ + + +\section{Processing the Command Line} + + +@<process the command line@>= +i=1; +while (i<argc) +{ char *option; + if (argv[i][0]=='-') + { option=argv[i]+1; + while (*option!=0) + { switch(*option) + { @<process options@>@; + default: commandline_error("unknown option"); + } + option++; + } + } + else if (input_file_name==NULL) + input_file_name=argv[i]; + else + commandline_error("multiple input files given"); + i++; +} +@ + + +Sometimes an error means that the program should also explain how to use it. + +@<functions@>= +void explain_usage(void) +{ fprintf(stderr, + "Use: "@; + "texprofile [-options] <input file>\n"@; + "options:\n"@; + @<explain general options@>@; + "\n" + @<explain table options@>@; + "\n" + @<explain format options@>@; + "\n" + ); + exit(1); +} +@ + +A simple option that most users will use occasionaly is the {\tt -?} option. +This option can also be look like {\tt --help}, {\tt -help} or {\tt -h}. + +@<process options@>= +case 'h': +case '?': explain_usage(); break; +@ + +@<explain general options@>= +"-? display this help and exit\n"@; +"--help display this help and exit\n"@; +@ + +The help option should also be available as a long option: + +@<process options@>= +case '-': + option++; + @<process long options@>@; + else commandline_error("unknown long option"); + break; +@ + +@<process long options@>= + if (strcmp(option,"help")==0) explain_usage(); +@ + +The only other long option currently supported is the {\tt --version} option. + +@d VERSION_STR "texprofile version 1.0" + +@<process long options@>= + if (strcmp(option,"version")==0) + { printf(VERSION_STR "\n"); + exit(1); + } +@ + +@<explain general options@>= +"--version output version information and exit\n"@; +@ + +\section{Error Handling} +If an error occurs this program will print an error message +and terminate. There is no attempt to recover from errors. + +@<error handling@>= +int error(char *msg) +{ fprintf(stderr,"texprofile: %s\n", msg); + exit(1); + return 0; +} +@ + +The program is a little bit more verbose if there is an error in the +command line. + +@<error handling@>= +int commandline_error(char *msg) +{ fprintf(stderr,"texprofile: %s\n", msg); + fprintf(stderr,"Try 'texprofile --help' for more information.\n"); + exit(1); + return 0; +} +@ + + + + +The input file should start with a marker: the ASCII codes of ``TEX PROF''. +The same marker is used later to separate the different sections of the file. + +@<functions@>= +void check_file_marker(char *msg) +{ char marker[8]; + if (fread(marker,1,8,in)!=8) + error("Unexpected end of input"); + if(strncmp(marker,FILE_MARKER,8)!=0) + error(msg); +} +@ + +\appendix + +\section{The Table of Command Names} +To print commands nicely, here is an array of command names. Some +of the commands also have names to be able to test for them; +the definitions are taken from {\tt texprof.w}. + +@d max_command 100 /*the largest command code seen at |big_switch|*/ + +@d system_cmd (max_command+1) /*pseudo command value*/ +@d system_profile_on (system_cmd+1) +@d system_profile_off (system_cmd+2) +@d system_macro_push (system_cmd+3) +@d system_macro_pop (system_cmd+4) +@d system_macro_pop_small (system_cmd+5) +@d system_macro_pop_0 (system_cmd+6) + +@<declarations@>= +static char *cmd_name[]= {@/ +"relax", /*do nothing ( \.{\\relax} )*/ +"left_brace", /*beginning of a group ( \.\{ )*/ +"right_brace", /*ending of a group ( \.\} )*/ +"math_shift", /*mathematics shift character ( \.\$ )*/ +"tab_mark", /*alignment delimiter ( \.\&, \.{\\span} )*/ +"car_ret/" /*end of line ( |carriage_return|, \.{\\cr}, \.{\\crcr} )*/ +"out_param", /*output a macro parameter*/ +"mac_param", /*macro parameter symbol ( \.\# )*/ +"sup_mark", /*superscript ( \.{\char'136} )*/ +"sub_mark", /*subscript ( \.{\char'137} )*/ +"ignore/" /*characters to ignore ( \.{\^\^@@} )*/ +"endv", /*end of \<$v_j$> list in alignment template*/ +"spacer", /*characters equivalent to blank space ( \.{\ } )*/ +"letter", /*characters regarded as letters ( \.{A..Z}, \.{a..z} )*/ +"other_char", /*none of the special character types*/ +"active/" /*characters that invoke macros ( \.{\char`\~} )*/ + "par/" /*end of paragraph ( \.{\\par} )*/ + "match", /*match a macro parameter*/ +"comment/" /*characters that introduce comments ( \.\% )*/ + "end_match/" /*end of parameters to macro*/ + "stop", /*end of job ( \.{\\end}, \.{\\dump} )*/ +"invalid_char/" /*characters that shouldn't appear ( \.{\^\^?} )*/ + "delim_num", /*specify delimiter numerically ( \.{\\delimiter} )*/ + +"char_num", /*character specified numerically ( \.{\\char} )*/ +"math_char_num", /*explicit math code ( \.{\\mathchar} )*/ +"mark", /*mark definition ( \.{\\mark} )*/ +"xray", /*peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)*/ +"make_box", /*make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)*/ +"hmove", /*horizontal motion ( \.{\\moveleft}, \.{\\moveright} )*/ +"vmove", /*vertical motion ( \.{\\raise}, \.{\\lower} )*/ +"un_hbox", /*unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )*/ +"un_vbox", /*unglue a box ( \.{\\unvbox}, \.{\\unvcopy} \dots )*/ + +"remove_item", /*nullify last item ( \.{\\unpenalty}, \dots )*/ +"hskip", /*horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)*/ +"vskip", /*vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)*/ +"mskip", /*math glue ( \.{\\mskip} )*/ +"kern", /*fixed space ( \.{\\kern} )*/ +"mkern", /*math kern ( \.{\\mkern} )*/ +"leaders/shipout", /*use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)*/ +"halign", /*horizontal table alignment ( \.{\\halign} )*/ +"valign", /*vertical table alignment ( \.{\\valign} )*/ +"no_align", /*temporary escape from alignment ( \.{\\noalign} )*/ +"vrule", /*vertical rule ( \.{\\vrule} )*/ +"hrule", /*horizontal rule ( \.{\\hrule} )*/ +"insert", /*vlist inserted in box ( \.{\\insert} )*/ +"vadjust", /*vlist inserted in enclosing paragraph ( \.{\\vadjust} )*/ +"ignore_spaces", /*gobble |spacer| tokens ( \.{\\ignorespaces} )*/ +"after_assignment", /*save till assignment is done ( \.{\\afterassignment} )*/ +"after_group", /*save till group is done ( \.{\\aftergroup} )*/ +"break_penalty", /*additional badness ( \.{\\penalty} )*/ +"start_par", /*begin paragraph ( \.{\\indent}, \.{\\noindent} )*/ +"ital_corr", /*italic correction ( \.{\\/} )*/ +"accent", /*attach accent in text ( \.{\\accent} )*/ +"math_accent", /*attach accent in math ( \.{\\mathaccent} )*/ +"discretionary", /*discretionary texts ( \.{\\-}, \.{\\discretionary} )*/ +"eq_no", /*equation number ( \.{\\eqno}, \.{\\leqno} )*/ +"left_right", /*variable delimiter ( \.{\\left}, \.{\\right} ) \dots )*/ +"math_comp", /*component of formula ( \.{\\mathbin}, etc.~)*/ +"limit_switch", /*diddle limit conventions ( \.{\\displaylimits}, etc.~)*/ +"above", /*generalized fraction ( \.{\\above}, \.{\\atop}, etc.~)*/ +"math_style", /*style specification ( \.{\\displaystyle}, etc.~)*/ +"math_choice", /*choice specification ( \.{\\mathchoice} )*/ +"non_script", /*conditional math glue ( \.{\\nonscript} )*/ +"vcenter", /*vertically center a vbox ( \.{\\vcenter} )*/ +"case_shift", /*force specific case ( \.{\\lowercase}, \.{\\uppercase}~)*/ +"message", /*send to user ( \.{\\message}, \.{\\errmessage} )*/ +"extension", /*extensions to \TeX\ ( \.{\\write}, \.{\\special}, etc.~)*/ +"in_stream", /*files for reading ( \.{\\openin}, \.{\\closein} )*/ +"begin_group", /*begin local grouping ( \.{\\begingroup} )*/ +"end_group", /*end local grouping ( \.{\\endgroup} )*/ +"omit", /*omit alignment template ( \.{\\omit} )*/ +"ex_space", /*explicit space ( \.{\\\ } )*/ +"no_boundary", /*suppress boundary ligatures ( \.{\\noboundary} )*/ +"radical", /*square root and similar signs ( \.{\\radical} )*/ +"end_cs_name", /*end control sequence ( \.{\\endcsname} )*/ +"min_internal/" /*the smallest code that can follow \.{\\the}*/ + "char_given", /*character code defined by \.{\\chardef}*/ +"math_given", /*math code defined by \.{\\mathchardef}*/ +"last_item", /*most recent item ( \.{\\lastpenalty},\dots )*/ + +"toks_register", /*token list register ( \.{\\toks} )*/ +"assign_toks", /*special token list ( \.{\\output}, \.{\\everypar}, etc.~)*/ +"assign_int", /*user-defined integer ( \.{\\tolerance}, \.{\\day}, etc.~)*/ +"assign_dimen", /*user-defined length ( \.{\\hsize}, etc.~)*/ +"assign_glue", /*user-defined glue ( \.{\\baselineskip}, etc.~)*/ +"assign_mu_glue", /*user-defined muglue ( \.{\\thinmuskip}, etc.~)*/ +"assign_font_dimen", /*user-defined font dimension ( \.{\\fontdimen} )*/ +"assign_font_int", /*user-defined font integer ( \.{\\hyphenchar}, \dots )*/ +"set_aux", /*specify state info ( \.{\\spacefactor}, \.{\\prevdepth} )*/ +"set_prev_graf", /*specify state info ( \.{\\prevgraf} )*/ +"set_page_dimen", /*specify state info ( \.{\\pagegoal}, etc.~)*/ +"set_page_int", /*specify state info ( \.{\\deadcycles}, \dots )*/ +"set_box_dimen", /*change dimension of box ( \.{\\wd}, \.{\\ht}, \.{\\dp} )*/ +"set_shape", /*specify fancy paragraph shape ( \.{\\parshape} ) \dots )*/ +"def_code", /*define a character code ( \.{\\catcode}, etc.~)*/ +"def_family", /*declare math fonts ( \.{\\textfont}, etc.~)*/ +"set_font", /*set current font ( font identifiers )*/ +"def_font", /*define a font file ( \.{\\font} )*/ +"internal_register", /*internal register ( \.{\\count}, \.{\\dimen}, etc.~)*/ +"advance", /*advance a register or parameter ( \.{\\advance} )*/ +"multiply", /*multiply a register or parameter ( \.{\\multiply} )*/ +"divide", /*divide a register or parameter ( \.{\\divide} )*/ +"prefix", /*qualify a definition ( \.{\\global}, \.{\\long}, \.{\\outer} etc.~) )*/ +"let", /*assign a command code ( \.{\\let}, \.{\\futurelet} )*/ +"shorthand_def", /*code definition ( \.{\\chardef}, \.{\\countdef}, etc.~)*/ +"read_to_cs", /*read into a control sequence ( \.{\\read}, etc.~)*/ +"def", /*macro definition ( \.{\\def}, \.{\\gdef}, \.{\\xdef}, \.{\\edef} )*/ +"set_box", /*set a box ( \.{\\setbox} )*/ +"hyph_data", /*hyphenation data ( \.{\\hyphenation}, \.{\\patterns} )*/ +"set_interaction", /*define level of interaction ( \.{\\batchmode}, etc.~)*/ + +"system", /*system start*/ +"profile on", /*switching on profile ( \.{\\profileon} )*/ +"profile off", /*switching on profile ( \.{\\profileoff} )*/ +"call", /*macro call*/ +"pop", /*macro return*/ +"pop n", /*macro return*/ +"pop 0", /*macro return*/ +"pop 1", /*macro return*/ +"pop 2", /*macro return*/ +"pop 3", /*macro return*/ +"pop 4", /*macro return*/ +"pop 5", /*macro return*/ +"pop 6", /*macro return*/ +"pop 7", /*macro return*/ +"pop 8", /*macro return*/ +"pop 9", /*macro return*/ +"pop 10", /*macro return*/ +"unknown" /*should not happen*/ +}; + +static const int cmd_num=sizeof(cmd_name)/sizeof(*cmd_name); + +#define CMD_NAME(N) ((N)<cmd_num?cmd_name[N]:"unknown") + +static char *line_name[]= {@/ /*for the system pseudo file*/ +"unknown", /*as it says*/ +"start", /*time before executing the first command*/ +"end", /*time after executing the last command*/ +"shipout", /*time spent writing the dvi file*/ +"linebrk", /*time spent on line breaking*/ +"initrie", /*time spent on setting up hyphenation*/ +"buildpg", /*time spent in |build_page|*/ +"inputln", /*time spent in |input_ln|*/ +"insert", /*time spent on tokens inserted by \TeX*/ +}; + +static char *every_name[]={@/ /* names for output, everypar, etc. */ +"output", /*for output routines*/ +"everypar", /*for \.{\\everypar}*/ +"everymath", /*for \.{\\everymath}*/ +"everydisplay", /*for \.{\\everydisplay}*/ +"everyhbox", /*for \.{\\everyhbox}*/ +"everyvbox", /*for \.{\\everyvbox}*/ +"everyjob", /*for \.{\\everyjob}*/ +"everycr", /*for \.{\\everycr}*/ +"mark", /*for \.{\\topmark}, etc.*/ +"everyeof" /*for \.{\\everyeof}*/ +}; + +static const int sys_line_num=sizeof(line_name)/sizeof(*line_name); +@ + +%\plainsection{References} + +%{\baselineskip=11pt +%\def\bfblrm{\small\rm}% +%\def\bblem{\small\it}% +%\bibliography{../hint} +%\bibliographystyle{plain} +%} + +%\plainsection{Index} +%{ +%\def\_{{\tt \UL}} % underline in a string +%\catcode`\_=\active \let_=\_ % underline is a letter +%\input texprofile.ind +%} + +\write\cont{} % ensure that the contents file isn't empty +% \write\cont{\catcode `\noexpand\@=12\relax} % \makeatother +\closeout\cont% the contents information has been fully gathered diff --git a/Build/source/texk/web2c/texprofdir/texprofilemac.tex b/Build/source/texk/web2c/texprofdir/texprofilemac.tex new file mode 100644 index 00000000000..dbfce0880a4 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/texprofilemac.tex @@ -0,0 +1,1535 @@ +% Copyright 2017-2024 Martin Ruckert, Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +% OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +% THE SOFTWARE. +% +% Except as contained in this notice, the name of the copyright holders shall +% not be used in advertising or otherwise to promote the sale, use or other +% dealings in this Software without prior written authorization from the +% copyright holders. + +\input idxmac.tex +\let\displayverbatimfont=\tt + +% V E R B A T I M . T E X +% +% THIS SET OF MACROs IS TAKEN FROM D.E.K.'s TeXBook +% AND PROVIDES AN EASY WAY OF TYPESETTING TEXTS VERBATIM: +% +% 1. \verbatim<character><text without that character><character> +% causes the text to be set verbatim using \tt font; +% 2. if the text uses up all the alphabet, \doubleverbatim macro +% can be used instead; this is similar to the previous one but +% a pair of characters is now used as a delimiter; +% 3. in case of emergency \tripleverbatim macro may be of help... +% +%--------------------------------------------- +\def\uncatcodespecials % see D.E.K., pp. 344 and 380 + {\def\do##1{\catcode`##1=12}\dospecials}% +%--------------------------------------------- +{\catcode`\^^I=\active \gdef^^I{\ \ \ \ }% TAB character is replaced by + % 4 spaces; it is better than + % nothing, but it does not mimic + % true tabbing satisfactorily---maybe + % some nice day... + \catcode`\`=\active\gdef`{\relax\lq}}% this line inhibits Spanish + % ligatures ?` and !` of \tt font +\def\setupverbatim % see D.E.K., p. 381 + {\tt % + \spaceskip=0pt \xspaceskip=0pt % just in case... + \catcode`\^^I=\active % + \catcode`\`=\active % + \def\par{\leavevmode\endgraf}% this causes that empty lines aren't + % skipped + \obeylines \uncatcodespecials \obeyspaces}% +{\obeyspaces \global\let =\ }% this causes that leading blanks aren't + % skipped; cf. also def's of \space, \endgraf, + % \lq, \obeyspaces, and \obeylines, + % D.E.K., pp. 351--352 +%--------------------------------------------- +% see D.E.K., p. 382 +\def\doverbatim#1{\def\next##1#1{##1\endgroup}\next}% +\def\verbatim{\begingroup\setupverbatim\doverbatim}% +%---------- +\def\dodoubleverbatim#1#2{\def\next##1#1#2{##1\endgroup}\next}% +\def\doubleverbatim{\begingroup\setupverbatim\dodoubleverbatim}% +%---------- +\def\dotripleverbatim#1#2#3{\def\next##1#1#2#3{##1\endgroup}\next}% +\def\tripleverbatim{\begingroup\setupverbatim\dotripleverbatim}% +%--------------------------------------------- + + + +%% +%% special treatment for @ +%% + +\def\makeatletter{\catcode`\@=11\relax} +\def\makeatother{\catcode`\@=12\relax} +\makeatletter + +%% +%% Conditionals +%% + +\newif\ifhint +\newif\ifbook +\newif\ifpdf + +% HINT +\expandafter\ifx\csname HINTversion\endcsname\relax + \hintfalse +\else + \hinttrue +\fi + +% PDF +\expandafter\ifx\csname pdfliteral\endcsname\relax + \pdffalse +\else + \pdftrue +\fi + +% Book +\expandafter\ifx\csname book\endcsname\relax + \ifhint\bookfalse + \else\ifpdf\bookfalse % This might change + \else\bookfalse + \fi\fi +\else +\booktrue +\hintfalse +\pdffalse +\fi + +\message{Book: \ifbook true \else false \fi} +\message{PDF: \ifpdf true \else false \fi} +\message{HINT: \ifhint true \else false \fi} + +%% +%% Additional fonts +%% + +\font\tenss=cmss10 % used for the HINT name +\font\tenssbx=cmssbx10 % used for the bold HINT name +\font\largess=cmss12 scaled\magstep1 % used for large HINT name +\font\largessbx=cmssbx10 scaled\magstep2 % used for large bold HINT name +\font\manual=manfnt % font used for the METAFONT logo, etc. +\let\sf\tenss +\def\bf{\let\sf\tenssbx\fam\bffam\tenbf} + + +%% Large Font for sections +%\font\largebf=cmb14 scaled\magstep0 +%\font\largebf=cmb12 scaled\magstep1 +\font\largebf=cmbx12 scaled\magstep1 +%\font\largebf=cmbx8 scaled\magstep3 +%\font\largeit=cmti14 scaled\magstep0 +\font\largeit=cmti12 scaled\magstep1 +%\font\largett=cmtt14 scaled\magstep0 +\font\largett=cmtt12 scaled\magstep1 +%\font\largerm=cmr14 scaled\magstep0 +\font\largerm=cmr12 scaled\magstep1 +% for mathbolditalic +%\font\tenmib=cmmib10 scaled\magstep0 +\font\tenmib=cmmib10 scaled\magstep0 +\font\largemib=cmmib10 scaled\magstep1 + +%% Small Fonts for Figure and Table descriptions +\font\figbf=cmbx9 scaled\magstep0 +\font\figit=cmti9 scaled\magstep0 +\font\figmit=cmmi9 scaled\magstep0 +\font\figsy=cmsy9 scaled\magstep0 +\font\figtt=cmtt9 scaled\magstep0 +\font\figrm=cmr9 scaled\magstep0 +% for mathbolditalic +\font\figmib=cmmib9 scaled\magstep0 + +\def\largebold{\let\bf\largebf\let\sf\largessbx\bf} + +\def\large{\def\rm{\fam0\largerm}\let\bf\largebold\let\it\largeit\let\tt\largett\let\mib\largemib\let\sf\largess% +\textfont0=\largerm\textfont1=\largeit +\setbox\strutbox=\hbox{\vrule height9.5pt depth5.0pt width\z@}\rm} + +\def\small{\def\rm{\fam0\figrm}\let\bf\figbf\let\it\figit\let\tt\figtt\let\mib\figmib% +\textfont0=\figrm\textfont1=\figmit\textfont2=\figsy% +\setbox\strutbox=\hbox{\vrule height9.0pt depth4.5pt width\z@}\rm} + +\let\tiny\eightrm +\let\mib\tenmib + + +%% +%% Logos +%% + +%\def\Pascal/{{\mc P\kern-.05emascal\spacefactor1000}} +\def\CEE{{\mc C\spacefactor1000}} +\def\Pascal{\leavevmode\hbox{\mc P\kern-.05emascal}} +\def\PDF{\leavevmode\hbox{\mc PDF}} +\def\WEB{\leavevmode\hbox{\tt WEB\spacefactor1000}} +\def\GNU{\leavevmode\hbox{\mc GNU}} +\def\Prote{{\tenrm P\kern-0.1em R\kern-0.15em\raise.11ex\hbox{o}% + \kern-0.22em T\kern-0.05em E}} +\def\eTeX{$\varepsilon$-\TeX} +\def\HINT{\leavevmode\hbox{\sf HINT\spacefactor1000}} +\def\cweb{\leavevmode\hbox{\tt cweb}} +\def\web2w{\leavevmode\hbox{\tt web2w}} +\def\LaTeX{L\kern-.36em\raise.3ex\hbox{\sc A}\kern-.15em\TeX}% +\def\MF{{\manual META}\-{\manual FONT}}% +\def\TL{\TeX~Live} +\def\kpse{\leavevmode\hbox{\tt kpathsearch\spacefactor1000}} +\def\TUB{{\sl TUGboat\/}} +\def\TUG{\TeX\ \UG} +\def\tug{\acro{TUG}} +\def\UG{Users Group} +\ifpdf +\sanitizecommand{\TeX}{TeX} +\sanitizecommand{\eTeX}{eTeX} +\sanitizecommand{\Prote}{PRoTE} +\fi +%% +%% Special controlsequences +%% + +\def\abs#1{\left|#1\right|} +\def\<#1>{$\langle\,$#1$\,\rangle$} +\def\registered{{\ooalign{\hfil\raise.07ex\hbox{\sevenrm R}\hfil\crcr\Orb}}} +\def\hair{\kern.05em\relax} % teeny tiny space +\def\center#1{\bigskip\line{\hfil#1\hfil}\bigbreak} +\chardef\VB=`\| % vertical bar character in a string +\def\^{\ifmmode\mathchar"222 \else\char`^ \fi}%from webmac.tex: pointer or hat +\ifhint +\def\_{{\tt\UL}} % make underline an ordinary character for better searching +\fi + +% Bibtex needs this +\def\mbox#1{\leavevmode\hbox{#1}} + +% URLs +{\gdef\urldot{.}\catcode`.=13 + \gdef\urlslash{/}\catcode`/=13 + \gdef\urlspecials{\def.{\urldot\penalty 0}\def/{\urlslash\penalty 0}} +} +\def\url{\bgroup\hskip 1cm plus 5cm\penalty -100\hskip -1cm plus -5cm\tt + \catcode`_=12\catcode`\~=12\catcode`.=13\catcode`/=13\urlspecials} +\def\endurl{\egroup} + + +% for the index +\def\see#1#2{\hskip 0pt plus 100pt\penalty 0\hskip 0 pt plus -100pt{\it see\/} #1} + +% typewriter type for strings +\def\.#1{\leavevmode\hbox{\tt + \let\\=\BS % backslash in a string + \let\{=\LB % left brace in a string + \let\}=\RB % right brace in a string + \let\~=\TL % tilde in a string + \let\ =\SP % space in a string + \let\_=\UL % underline in a string + \let\&=\AM % ampersand in a string + \let\^=\CF % circumflex in a string + \let\|=\VB % vertical bar in a string + #1\kern.05em}} + +%{\rm\gdef\strutdepth{\dp\strutbox}} + + +%% +%% Environments +%% + +% Quotations (form the standard) +\def\beginquote{\bgroup\narrower\noindent\sl} +\def\endquote{\egroup} + +% Enumerate +\newcount\enum +%other styles use \medskip instead of \par +\def\enumerate{\par\bgroup\advance\leftskip by\parindent\enum=0% + \def\item{\advance\enum by 1\par\enumdest\smallskip\noindent\hbox to 0pt{\hss\the\enum.~}\ignorespaces}} +\def\endenumerate{\medskip\egroup\noindent} + +%Itemize +\def\itemize{\par\bgroup\advance\leftskip by\parindent\relax% + \def\item{\par\smallskip\noindent\llap{$\bullet$\enspace}\ignorespaces}} +\def\enditemize{\medskip\egroup\noindent} + +%Float +\long\def\float#1{\midinsert\parindent 0pt\relax#1\endinsert} +%\def\float#1{\midinsert +%\hsize\pagewidth +%\parindent 0pt\vbox{\hbox{\kern-\marginwidth\vbox{#1}}}\endinsert} + +%% +%% Redefining cwebmac.tex macros +%% + +%defining how to output terminals and nonterminals +\def\ts#1{\ifmmode\hbox{\sc #1}\else{\sc #1}\fi}% terminal symbol +\def\nts#1{{\sl #1\/}}% nonterminal symbol +% redefining the box around verbatim code +\def\vb#1{\hbox{\strut\.{#1}}} +% regular expressions and actions +\newbox\rebox +\def\re#1{\leavevmode\setbox\rebox\hbox{#1\hfill}\ifdim\wd\rebox<7em\wd\rebox=7em\fi\box\rebox\quad} +\def\ac{\global\ind=10\hangindent=\ind em\relax} +\def\eac{\global\ind=3\hangindent=\ind em\relax} +% yskip is usually a smallskip = 3pt pus 1pt minus 1pt +% I give it some extra stretchability +%\def\yskip{\vskip 3pt plus 3pt minus 1pt} +\let\yskip\smallbreak + +% redefine \5 for an optional break without the 2.5em reduced indentation +%\def\5{\hskip 0pt plus 2.5em\penalty-1\hskip 0pt plus -2.5em\kern0.5em\ignorespaces}% optional break +%redefine \5 to give some shrinkability +% from hitex and hint style: +%\def\5{\hfil\penalty-1\hskip 0pt minus 6pt\hfilneg\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\5{\hfil\penalty10\hfilneg\hskip 0pt minus 6pt\kern2.5em\copy\bakk\ignorespaces}% optional break +\def\J{}% dont show @& in the tex output + + +\def\Y{\par\setsecstartref\yskip} + +% start sections +\def\stsec{\rightskip=0pt % get out of C mode (cf. \B) + \sfcode`;=1500 \pretolerance 200 \hyphenpenalty 50 \exhyphenpenalty 50 % +} + +% the box for the points to operator "->" in C +\setbox\MGbox=\hbox{$\rightarrow$} + +% how to display NULL +\def\NULL{\tt NULL} + +%other styles make this a \par +\def\note#1#2.{} +%\Y\noindent{\hfill% +% \baselineskip10pt\tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.\par}} + +% put the section number to the right +\newif\ifshowsecstartref \showsecstartreffalse +\newif\ifsetsecstartref \setsecstartreffalse + +\def\secstartref{% used in \MN + \ifshowsecstartref + \vadjust{\vtop to 0pt{\vss\hbox to \hsize{\hfill\tiny(\secstar)}\kern 0pt}} + \global\showsecstartreffalse + \fi +} + +\def\setsecstartref{% used in \Y + \ifsetsecstartref + \codedest + \global\setsecstartreffalse + \fi +} + + +%other styles make this minus 10pt +\def\B{\rightskip=0pt plus 100pt minus 0pt % go into C mode + \sfcode`;=3000 + \pretolerance 10000 + \hyphenpenalty 1000 % so strings can be broken (discretionary \ is inserted) + \exhyphenpenalty 1000 + \global\ind=2 \1\ \unskip +} + +% formating of C comments +\def\C#1{\5\hfill$/\ast\,${\cmntfont #1}$\,\ast/$} + +% section begin +\def\M#1{\MN{#1}\ifon\stsec\smallskip +%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph +\noindent\ignorespaces}% beginning of section + +\def\N#1#2#3.{\MN{#1}\ifon\stsec\smallskip +%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph +\noindent{\bf#3\quad}\ignorespaces}% beginning of section + +\def\MN#1{\smallskip + {\xdef\secstar{#1}\let\*=\empty\xdef\secno{#1}}% + \gdef\thecode{#1}% common code for \M, \N + \ontrue\showsecstartreftrue\setsecstartreftrue} + +% Used in section text +\def\U{\rightnote{Used in}} % xref for use of a section +\def\Us{\rightnote{Used in}} % xref for uses of a section +% replacing \note in \U and \Us by \rightnote +%\def\rightnote#1#2.{\vskip-\baselineskip\vtop to 0pt{\vss\hbox to \hsize{\hfill +% \tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.}\kern 0pt}} +%\def\rightnote#1#2.{\vskip-\baselineskip\hbox to \hsize{\hfill +% \tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.}} +%\def\rightnote#1#2.{\penalty1000\discretionary{}{\hbox{}}{\kern 2em}\penalty1000\hfill +% \hbox{\tiny#1~\ifacro{\pdfnote#2.}\else#2\fi.}} +\def\rightnote#1#2.{% + \penalty1000\discretionary{}{\hbox{}}{\kern 4em}\penalty1000\hfill + \hskip -4em plus 4em\hbox{\tiny #1~\codenote#2.}.} + +\def\X#1:#2\X{\setsecstartref + \ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi %$% section name + \XX$\langle\,${\let\I=\ne#2\kern.5em\coderef{#1}$\rangle$\XX}% + \secstartref} + + +% how to display hex numbers +\def\hex{\hbox{$^{\scriptstyle\#}$\tt\aftergroup}} % CWEB style + +\def\(#1){} % this is used to make section names sort themselves better + + +%% +%% Dimensions to achive the desired layout +%% + + +% the other variables are defined in cwebmac.tex +\newdimen\headheight +\newdimen\footheight +\newdimen\leftmargin +\newdimen\rightmargin +\newdimen\topmargin +\newdimen\bottommargin +\newdimen\marginwidth + +\parskip 0pt plus .8pt + +\leftmargin=22mm +\rightmargin=22mm +\topmargin=15mm +\bottommargin=27mm +\marginwidth=0pt % we do not have notes in the margin + +\headheight=12pt % Text in Head +\advance\headheight+13pt % Abstand 1pt, Linie 0.47pt und Abstand zum Text 11.53pt +\footheight=0pt % Text in Footer + +\pagewidth=6.69in +\advance\pagewidth-\leftmargin % left margin/offset +\advance\pagewidth-\rightmargin % left margin/offset + +% 6.69in = 169.926 +% - 2*22mm = 125.926mm +% +% 9.61in = 244.094 +% - 15mm -27 mm = 202.094 +% (12+13)pt = 8.786 mm +% -8.786 = 193.308 mm + +\fullpageheight=9.61in +\advance\fullpageheight-\topmargin% topmargin +\advance\fullpageheight-\bottommargin% bottommargin + +\pageheight=\fullpageheight +\advance\pageheight-\headheight +\advance\pageheight-\footheight + +\def\setpage{\hsize\pagewidth\advance\hsize-\marginwidth\vsize\pageheight} % use after changing page size + +\setpage + +% setting the papersize for postscript and pdf + +\ifbook + %\overfullrule=0pt + \pdfpageheight=9.61in + \pdfpagewidth=6.69in + \pdfhorigin=\leftmargin + \pdfvorigin=\topmargin + \pdfcompresslevel=9 + \pdfdecimaldigits=4 + \pdfpkresolution=1200 + \pdfimageresolution=1200 +\else\ifpdf + \pdfpageheight=9.61in + \pdfpagewidth=6.69in + \pdfhorigin=\leftmargin + \pdfvorigin=\topmargin + \pdfcompresslevel=9 + \pdfdecimaldigits=4 + \pdfpkresolution=1200 + \pdfimageresolution=1200 +\else + \special{papersize=6.69in,9.61in}% postscript und ps2pdf +\fi\fi + + + +%% +%% Page layout +%% + +\long\def\leftmark#1#2{#1} +\long\def\subsectionmark#1#2{#2} +\long\def\rightmark#1#2{\if0#1\else\expandafter\subsectionmark\botmark\fi} + +\def\lheader{\mainfont\strut +\thepageno\hfill\expandafter\leftmark\firstmark} % top line on left-hand pages + +\def\rheader{\mainfont\strut +\expandafter\rightmark\firstmark\hfill\thepageno} % top line on right-hand pages + +\let\page=\pagebody +%\raggedbottom +\normalbottom +%\def\page{\box255 }\normalbottom % faster, but loses plain TeX footnotes +\def\normaloutput#1#2#3{\ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox{ + \vbox to\fullpageheight{\pagelabel + \iftitle\global\titlefalse + \else\hbox{\vbox to \headheight{ + \hbox to \pagewidth{\ifodd\pageno #3\else#2\fi} + \vskip 1pt\relax + \nointerlineskip + \hrule height .47pt +% \hbox{\hrule}%fill\psfig{file=image/topline.eps}\hfil}% + \vfil}}% + \fi + \nointerlineskip + \hbox{\kern\marginwidth\vbox to \pageheight{#1}}}} % parameter #1 is the page itself + \global\advance\pageno by1} + +\def\nomarginoutput#1#2#3{% + \ifodd\pageno\hoffset=\pageshift\fi + \shipout\vbox to\fullpageheight{\pagelabel\nointerlineskip + \iftitle\global\titlefalse + \hbox{\vbox to \headheight{\vfil}} + \else + \hbox{\vbox to \headheight{\nointerlineskip + \hbox to \pagewidth{\ifodd\pageno#3\else#2\fi}% + \vskip 1 pt + \nointerlineskip + \hrule height .47pt +% \hbox{\psfig{silent=,bbllx=0pt,bblly=0pt,bburx=368pt,bbury=2pt,file=image/topline.eps}\hfil}% + \vfil}}% + \fi\nointerlineskip #1 % parameter #1 is the page itself +% \vss +% \hbox to \pagewidth{\ifodd\pageno\else#2\fi}% +% \nointerlineskip +% \hbox{\psfig{file=image/topline.eps}\hfil}% +}% +\global\advance\pageno by1} + +%% switch to global nomargin +\let\normaloutput\nomarginoutput +\def\sectionbox#1{\hbox{#1}} +\marginwidth=0mm +\setpage + + +%% +%% Frontmatter and mainmatter +%% +\pageno=1 +\def\thepageno{\romannumeral\pageno} +\def\frontmatter{\pageno=4\def\thepageno{\romannumeral\pageno}} +\def\mainmatter{ +\mark{{\sectionname}{\subsectionname}} +%\message{Mark {\sectionname}{\subsectionname}} +\vfil\break\ifodd\pageno\pageno=1\else\pageno=0\fi +\def\thepageno{\the\pageno}% +\sectioncount=0 +\mark{{0}{0}}% +%\hbox{Mark empty empty}% +\gdef\sectionname{} +\gdef\subsectionname{} +\gdef\codetitle{} +\gdef\subcodetitle{} +} + + + + +%% +%% Sections +%% + +\newcount\sectioncount +\sectioncount=0 +\newcount\subsectioncount +\subsectioncount=0 +\newcount\subsubsectioncount +\subsubsectioncount=0 + +\newskip\abovesecskip +\newskip\belowsecskip +\newskip\abovesubsecskip +\newskip\belowsubsecskip + +%\abovesecskip= 6ex plus 1ex minus .2ex %space above the section +%\belowsecskip=3.7ex plus .2ex% space after section +\abovesecskip= 0.15\vsize plus 5ex minus 1ex %space above the section +\belowsecskip=10pt plus 2pt% space after section +\abovesubsecskip=9pt plus 4pt minus 2pt%space above the section +\belowsubsecskip=3pt plus 1pt minus 0.5pt% space after section + + +\newif\ifappendix +\appendixfalse + +\mark{{0}{0}}\vskip 0pt minus 100pt +%\hbox{Mark empty empty}% +\def\thesection{\the\sectioncount} +\def\topsection{\the\sectioncount} + +%\def\sectionbox#1{\hskip-\marginwidth\hbox to \marginwidth{#1\hfil}} + +\def\heading#1#2{% +\def\secno{{\noindent\large\bf\strut{#1}#2}}% +\par\ifhmode\unskip\fi% end paragraph and remove vertical space +\penalty-500 +\vskip 0pt plus 72pt%allow some empty space at the bottom +\penalty-500 +\vskip 0pt plus -72pt% room for stetching and a page break +\hbox{}\vskip\abovesecskip% space above the section +%%\titletrue % omits page header for section +%\hbox{Mark null null}% +\mark{{0}{0}}% +\secno% The section title +\mark{{\sectionname}{\sectionname}}% +%\message{Mark heading {\sectionname}{\subsectionname}} +\nobreak\vskip\belowsecskip% space after section +\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph +\ignorespaces} + + + +\newcount\plaincount +\def\plainsection#1{% +\mark{{\sectionname}{\subsectionname}}% +%\message{Mark plain {\sectionname}{\subsectionname}} +\subsubsectioncount=0\subsectioncount=0% +\advance\plaincount by 1\relax +\def\thesection{} +\sectionpage% +\gdef\sectionname{#1}% +\gdef\subsectionname{#1}% +\gdef\codetitle{#1}% +\gdef\subcodetitle{#1}% +\tocsection{0}{}{#1} +\heading{}{#1}% +} + + +\gdef\sectionname{} +\gdef\subsectionname{} +\gdef\codetitle{}% +\gdef\subcodetitle{} + +\def\sectionpage{% +\vfil\break +\ifodd\pageno\else +\hbox{}% +\titletrue +\fi +\vfil\break +} + + +\def\section#1{% +\mark{{\sectionname}{\subsectionname}}% +%\message{Mark section {\sectionname}{\subsectionname}} +\subsubsectioncount=0\subsectioncount=0% +\advance\sectioncount by 1%updating counts +\let\thesection\topsection +\sectionpage% +\gdef\sectionname{\topsection\quad#1}% +\gdef\subsectionname{\thesection\quad#1}% +\gdef\codetitle{#1}% +\gdef\subcodetitle{#1}% +\ifnum\sectioncount=1\ifappendix\tocsection{0}{}{Appendix}\heading{}{Appendix}\fi\fi +\tocsection{0}{\thesection}{#1}% +\heading{\thesection~}{#1}% +} + + +\def\subsection#1{% +\subsubsectioncount=0\advance\subsectioncount by 1%updating counts +\def\thesection{\topsection.\the\subsectioncount} +\def\secno{{\noindent\bf\strut\sectionbox{\thesection~}#1}}% +\par\ifhmode\unskip\fi% end paragraph and remove vertical space +\tocsection{1}{\thesection}{#1}% +\penalty-200 +\vskip 0pt plus 36pt% allow some empty space at the bottom +\penalty-200 +\vskip 0pt plus -36pt% room for stetching and a page break +\gdef\subsectionname{\thesection\quad#1}% +\mark{{\sectionname}{\subsectionname}}% +%\message{Mark subsection {\sectionname}{\subsectionname}} +\vskip\abovesubsecskip% space above the subsection +\secno% The subsection title +\gdef\subcodetitle{#1}% +%\mark{{\sectionname}{\subsectionname}}% +%\hbox{Mark {\sectionname}{\subsectionname}}% +\nobreak\vskip\belowsubsecskip% space after subsection +\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph +\ignorespaces} + + +\def\subsubsection#1{% +%\mark{{\sectionname}{\subsectionname}}% +%\hbox{Mark {\sectionname}{\subsectionname}}% + \advance\subsubsectioncount by 1%updating counts +\def\thesection{\topsection.\the\subsectioncount.\the\subsubsectioncount} +\def\secno{{\noindent\it\strut\sectionbox{\thesection~}#1}}% +\par\ifhmode\unskip\fi% end paragraph and remove vertical space +\tocsection{2}{\thesection}{#1}% +\penalty-200 +\vskip 0pt plus 36pt% allow some empty space at the bottom +\penalty-200 +\vskip 0pt plus -36pt% room for stetching and a page break +\vskip\abovesubsecskip% space above the subsection +\secno% The subsubsection title +\nobreak\vskip\belowsubsecskip% space after subsection +%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph +%\noindent +\ignorespaces} + +%% +%% Appendix +%% + +\def\Alphanum#1{% + \ifcase#1\or A\or B\or C\or D\or E\or F\or G\or H\or I\or J\or + K\or L\or M\or N\or O\or P\or Q\or R\or S\or T\or U\or V\or W\or X\or + Y\or Z\else\number#1\fi} + +\def\appendix{% +\mark{{\sectionname}{\subsectionname}}% +%\message{Mark appendix {\sectionname}{\subsectionname}} +\sectioncount=0 +\subsectioncount=0 +\gdef\thesection{\Alphanum\sectioncount}% +\gdef\topsection{\Alphanum\sectioncount}% +\appendixtrue +} + +%% +%% Crossreference of Identifiers and Sections +%% +\iftrue %% No Crossreference of identifiers + \def\inx{\def\lr{L}} % this tells whether the left or right column is next +\else +\def\inx{% + \hsize=\pagewidth + \def\page{\box255 } \normalbottom + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse + \plainsection{Crossreference of Identifiers}% we are beginning the index + \mark{{0}{0}}% +%\hbox{Mark empty empty}% + \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\nomarginoutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse + \mark{{Crossreference of Identifiers}{Crossreference of Identifiers}}% + \fi} + \parfillskip 0pt plus 1fil + \let\topsecno=\nullsec + \message{Crossreference of Identifiers:} + \parskip 0pt plus .5pt + \outer\def\I##1, ##2.{\par\hangindent2em\noindent##1:\kern1em + \ifbook##2\else\ifpdf\pdfnote##2.\else##2\fi\fi.} % index entry + \def\[##1]{$\underline{##1}$} % underlined index item + \rm \rightskip0pt plus 2.5em \tolerance 10000 \let\*=\lapstar + \hyphenpenalty 10000 \parindent0pt + \small + \readindex +} +\fi + +\def\fin{% + \if L\lr\mark{{}{}}\fi + \vfill\eject % complete the current column. + \mark{{}{}} + \if R\lr\null\vfill\eject\fi % if necessarry add a right column + \setpage + \def\page{\box255 } \normalbottom + \output={\nomarginoutput\page\lheader\rheader} +} +\ifbook +\let\crosssections=\relax +\else +\def\crosssections{ + \plainsection{Crossreference of Code}% this is done when we are ending the index + \parindent 0pt + \parfillskip 0pt plus 1fil + \let\topsecno=\nullsec + \message{Crossreference of Code:} + \def\note##1##2.{\hfil\penalty-1\hfilneg\quad{\tiny##1~\ifbook##2\else + \ifpdf{\pdfnote##2.}\else + \ifhint\codenote##2.\else##2.\fi\fi\fi}} + \def\Q{\note{Cited in section}} % crossref for mention of a section + \def\Qs{\note{Cited in sections}} % crossref for mentions of a section + \def\U{\note{Used in}} % crossref for use of a section + \def\Us{\note{Used in}} % crossref for uses of a section + \def\I{\par\hangindent 2em}\let\*=* + \def\X##1:##2\X{\ifmmode\gdef\XX{\null$\null}\else\gdef\XX{}\fi %$% section name + \XX$\langle\,${\let\I=\ne##2}$\,\rangle$\XX\quad{\tiny Defined in~\codenote##1.}} + \readsections +} +\fi + + + +%% +%% Table of Content +%% + +% we separate the identification of a section used in links +%from the identification on paper, because plain sections dont +%have the latter. +\newcount\toccount +\toccount=0 + +\def\tocsection#1#2#3{% depth sectionnumber sectiontitle +\global\advance\toccount by 1% +\newdest{SC.\the\toccount}% +\ifhint + \immediate\write\cont{\noexpand\ZZ {#1}{#2}{#3}% write to contents file + {\noexpand\thepageno}{\the\toccount}} +\else +\edef\next{\write\cont{\noexpand\ZZ {#1}{#2}{#3}% write to contents file + {\noexpand\thepageno}{\the\toccount}}}\next % \tocline{depth}{sec}{title}{page}{toccount} +\fi +} + +\output{ + \ifhint + \shipout\box255\relax% otherwise I loose open, write, or close commands + \else + \setbox0=\page % the first page is garbage + \fi +% \openout\cont=\contentsfile +% \write\cont{\catcode `\noexpand\@=11\relax} % \makeatletter + \global\output{\normaloutput\page\lheader\rheader}} + + +\newbox\tocbox +\def\maketoc{% + \typeout{Reading table of contents} + \setbox\tocbox\vbox{% + \let\ZZ=\tocline + \ifbook\else\ifpdf\startpdf\fi\fi + \readcontents\relax + } + \typeout{Writing \contentsfile} + \immediate\openout\cont=\contentsfile\relax + \immediate\write\cont{\catcode `\noexpand\@=11\relax} +} + +\def\tableofcontent{\plainsection{Contents}\ifhint\HINTdest name {HINT.home}\fi +\unvbox\tocbox +} + + +\def\tocline#1#2#3#4#5{% depth sectionnumber sectiontitle page toccount +\ifnum#1=0 \smallskip\vskip 0pt plus 12pt\penalty -100\vskip 0pt plus -12pt\fi +\noindent +\line{% +\ifnum#1=0% + \hbox to 2em{\bf#2\hfill}{\bf#3~}\hfill + \hbox to 2em{\hss\bf\tocref{#1}{#3}{#4}{#5}}% +\else \ifnum#1=1% + \kern 2em\hbox to 3em{\rm#2\hfill}{\rm#3~}\leaders\hbox to .5em{.\hfil}\hfill + \hbox to 2em{\hss\tocref{#1}{#3}{#4}{#5}}% +\else + \kern 2em\hbox to 5em{\rm#2\hfill}{\it#3~}\leaders\hbox to .5em{.\hfil}\hfill + \hbox to 2em{\hss\tocref{#1}{#3}{#4}{#5}}\fi\fi +}} + + + +%% +%% Figures and Tables +%% + +\def\fig#1{\global\advance\figcount by 1% +\ifvmode\noindent\fi +\def\captype{Fig}% +\capskip=1em% +\vbox{\nointerlineskip\figdest +\hbox{\noindent#1\hskip -2pt\hskip 0pt plus 2pt minus 2pt}% +\medskip +\rlap{\small\unhbox\capbox}% +}} + +\def\tab#1{% +\ifvmode\noindent\fi +\def\captype{Tab}% +\capskip=0pt% +\vbox{#1\medskip\rlap{\small\unhbox\capbox}}} + +\def\table#1#2{\global\advance\tabcount by 1% +\ifvmode\noindent\fi +\def\captype{Tab}% +\capskip=0pt% +\vbox{\offinterlineskip\tabdest + \halign{\large\strut + \vrule##&&\quad\hfil##\hfil\quad\vrule\cr + \noalign{\hrule}#2\noalign{\hrule}}% + \medskip + \tabcaption{#1}% + \rlap{\small\unhbox\capbox}% + }% +} + +\def\captype{} +\newdimen\capskip +\capskip=1em +\newbox\capbox +\setbox\capbox\hbox{} +\newcount\figcount +\figcount=0 +\newcount\tabcount +\tabcount=0 + +\def\nextfigcaption#1{\small\strut\it\hskip\capskip\figindex{\captype}{\the\figcount}{#1}\captype.~\the\figcount:} + +\def\caption#1{\global\setbox\capbox\hbox{\nextfigcaption{#1} #1}} + +\def\nexttabcaption#1{\small\strut\it\hskip\capskip\tabindex{\captype}{\the\tabcount}{#1}\captype.~\the\tabcount:} +\def\tabcaption#1{\global\setbox\capbox\hbox{\nexttabcaption{#1} #1}} + + +%\long\def\leftfig#1#2#3{% +%\ifvmode\noindent\fi +%\def\captype{Fig}% +%\capskip=1em% +%\vbox{% +% \tabskip=0pt\halign to \hsize{% +% \vtop{\vskip 0pt\parskip=0pt\hsize=62.5mm\relax +% ##}\tabskip=5mm plus 2pt minus 2pt& +% \vtop{\vskip 0pt\parskip=0pt\hsize=62.5mm\relax##}\tabskip=0pt\cr +% #1&\capskip=0pt\nextfigcaption{#2} #3\cr}\medbreak}} + +\long\def\leftright#1#2#3#4{% +\ifvmode\noindent\else\par\noindent\fi +\vbox{\nointerlineskip + \advance\hsize by -\leftskip\advance\hsize by -\rightskip% + \leftskip=0pt\rightskip=0pt% + \hbox to \hsize{\strut + \raise\ht\strutbox + \vtop{\vskip 0pt\parskip=0pt\hsize=#1\hsize\advance\hsize by -2.5mm\relax + \noindent\strut\ignorespaces#3\vfill}\hfil + \raise\ht\strutbox + \vtop{\vskip 0pt\parskip=0pt\hsize=#2\hsize\advance\hsize by -2.5mm\relax + \noindent\strut\ignorespaces#4\vfill}}}} + + +\long\def\leftfig#1#2#3{% +\def\captype{Fig}% +\capskip=0pt% +\leftright{0.5}{0.5}{#1\kern -2pt\hskip 2pt plus 2pt minus 2pt}{\nextfigcaption{#2}\strut #3}} + + +\def\subindex#1{% + \par\ifhmode\unskip\fi% end paragraph and remove vertical space + \vskip\abovesubsecskip% space above the subsection + \vskip 0pt plus 72pt% allow some empty space at the bottom + \penalty-200\vskip 0pt plus -72pt % room for stetching and a page break + \noindent{\bf\strut#1}% + \nobreak\vskip\belowsubsecskip% space after subsection + \everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph + \ignorespaces +} + +\newwrite\@figfile +\newwrite\@tabfile + +\def\makefigindex{% + \immediate\openout\@figfile=\jobname.fig\relax + \def\figindex{\@bsphack\begingroup\@sanitize\@wrfigindex\@figfile}% + \immediate\openout\@tabfile=\jobname.tab\relax + \def\tabindex{\@bsphack\begingroup\@sanitize\@wrfigindex\@tabfile}% + \typeout{Writing index files \jobname.fig \jobname.tab} +} + +\newdimen\iboxsize +\def\thefigindex{% + + \immediate\closeout\@figfile\relax + \immediate\closeout\@tabfile\relax + %\small\baselineskip=11pt + \plainsection{List of Figures and Tables} + \makeatletter + \iboxsize=3.7em\relax + \subindex{Figures} + \def\indexentry##1##2##3##4{% captype, fig/tab count, caption, page + \noindent + \line{\hbox to \iboxsize{% + \rm##1.~##2:\hfil}{\rm ##3}\ \leaders\hbox to .5em{.\hfil}% + \hfill\hbox to 1.4em{\hss + \ifbook##4\else + \ifpdf + \pagelnk{##4}% + \else\ifhint\figlink{##2} + \else##4\fi\fi\fi + }}\par + }% + \input\jobname.fig\relax + \iboxsize=3.9em\relax + \subindex{Tables} + \def\indexentry##1##2##3##4{% captype, fig/tab count, caption, page + \noindent + \line{\hbox to \iboxsize{% + \rm##1.~##2:\hfil}{\rm ##3}\ \leaders\hbox to .5em{.\hfil}% + \hfill\hbox to 1.4em{\hss + \ifbook##4\else + \ifpdf + \pagelnk{##4}% + \else\ifhint\tablink{##2} + \else##4\fi\fi\fi + }}\par + }% + \input\jobname.tab\relax + \makefigindex +} + + +\def\@wrfigindex#1#2#3#4{% file, captype, fig/tab count, caption + \xdef\gtempa{\write#1{\string\indexentry{#2}{#3}{#4}{\noexpand\thepageno}}}\endgroup\gtempa + \if@nobreak\ifvmode\@nobreak\fi\fi\@esphack} + +%% +%% Format Definitions +%% + +%\newbox\codebox +\newwrite\@getfile +\newwrite\@putfile +\newwrite\@redfile +\newwrite\@wrtfile + +\def\makecode{% + \immediate\openout\@getfile=\jobname.get\relax + \immediate\openout\@putfile=\jobname.put\relax + \immediate\openout\@redfile=\jobname.red\relax + \immediate\openout\@wrtfile=\jobname.wrt\relax + \typeout{Writing index files \jobname.get \jobname.put \jobname.red \jobname.wrt} +} + +\def\thecodeindex{% + \immediate\closeout\@getfile\relax + \immediate\closeout\@putfile\relax + \immediate\closeout\@redfile\relax + \immediate\closeout\@wrtfile\relax + \def\indexentry##1##2##3##4{% codecount, depth, pagenumber, section + \ifnum##2=0 \smallskip\vskip 0pt plus 12pt\penalty -100\vskip 0pt plus -12pt\fi + \noindent + \line{% + \ifnum##2=0##4\hfill + \else\qquad\rm##4\ \leaders\hbox to .5em{.\hfil}\hfill + \hbox to 1.4em{\hss\pagelnk{##3}} + \fi + }\par + }% + \section{Format Definitions} + \makeatletter + \subsection{Reading the Long Format}\label{codeindex} + \input\jobname.red.srt\relax + \subsection{Writing the Long Format} + \input\jobname.wrt.srt\relax + \subsection{Reading the Short Format} + \input\jobname.get.srt\relax + \subsection{Writing the Short Format} + \input\jobname.put.srt\relax +} + + +\newcount\codecount +\codecount=0 + +\ifhint +\def\@wrcodeindex#1#2#3#4{% file, depth, pagenumber, [sub]sectionname + \global\advance\indexcount by 1\indexdest + \xdef\gtempa{\write#1{\string\indexentry{#3}{#2}{\the\indexcount}{#4}}}\endgroup\gtempa\@esphack +} +\else +\def\@wrcodeindex#1#2#3#4{% file, depth, pagenumber, [sub]sectionname + \xdef\gtempa{\write#1{\string\indexentry{#3}{#2}{\noexpand\thepageno}{#4}}}\endgroup\gtempa\@esphack +} +\fi + +\def\getindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@getfile}% +\def\putindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@putfile}% +\def\redindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@redfile}% +\def\wrtindex{\@bsphack\begingroup\@sanitize\@wrcodeindex\@wrtfile}% + + +\def\codelabel#1#2#3{ + \getindex{#1}{#2}{#3}% + \putindex{#1}{#2}{#3}% + \redindex{#1}{#2}{#3}% + \wrtindex{#1}{#2}{#3}} +\def\getsymbol{$\cdots\Longrightarrow$} +\def\putsymbol{$\Longrightarrow\cdots$} +\def\redsymbol{$-{}-{}-{}\Longrightarrow$} +\def\wrtsymbol{$\Longrightarrow{}-{}-{}-$} +\def\getcode{\codesection{\getsymbol}{Reading the short format}\getindex{1}{\the\codecount}{\subcodetitle}} +\def\putcode{\codesection{\putsymbol}{Writing the short format}\putindex{1}{\the\codecount}{\subcodetitle}} +\def\writecode{\codesection{\wrtsymbol}{Writing the long format}\wrtindex{1}{\the\codecount}{\subcodetitle}} +\def\readcode{\codesection{\redsymbol}{Reading the long format}\redindex{1}{\the\codecount}{\subcodetitle}} +\newdimen\coderule +\def\codesection#1#2{% + \par\ifhmode\unskip\fi% end paragraph and remove vertical space + \penalty-200 + \vskip 0pt plus 72pt% allow some empty space at the bottom + \penalty-200 + \vskip 0pt plus -72pt% room for stetching and a page break +\vskip\abovesubsecskip% space above the subsection +%\line{\setbox0=\hbox{#1}% +%\coderule=\hsize\advance\coderule by -\wd0\advance\coderule by -2em% +%$\vcenter{\hrule width \coderule}$\hss\box0} +\line{{\it #2\/}:\hfill#1} +\nobreak\vskip\belowsubsecskip\nobreak% space after subsection +%\everypar{{\setbox0=\lastbox}\everypar{}}% no indentation in the next paragraph +%\noindent +\ignorespaces} + +\def\hascode{\global\advance\codecount by 1% +\codelabel{0}{\the\codecount}{\codetitle}}% + + +%% +%% Makeindex and the Index +%% + +\newwrite\@indexfile + +% new jobname for index file +\def\makeindex{ + \immediate\openout\@indexfile=\jobname.inx% + \def\index{\@bsphack\begingroup\@sanitize\@wrindex\@indexfile}% + \typeout{Writing index file \jobname.inx }% +} + +\def\@idxitem{\par\hangindent 40pt} + +\newif\ifnextindex +\nextindexfalse + +\def\beginindex{% +\ifhint\relax\else + \def\page{\box255} \normalbottom + \output{\ifpagesaved\normaloutput{\box\sbox}\lheader\rheader\fi + \global\setbox\sbox=\page \global\pagesavedtrue} + \pagesavedfalse \eject % eject the page-so-far and predecessors + \setbox\sbox\vbox{\unvbox\sbox} % take it out of its box + \vsize=\pageheight \advance\vsize by -\ht\sbox % the remaining height + \hsize=.5\pagewidth \advance\hsize by -10pt + % column width for the index (20pt between cols) + \parfillskip 0pt plus .6\hsize % try to avoid almost empty lines + \def\lr{L} % this tells whether the left or right column is next + \output{\if L\lr\global\setbox\lbox=\page \gdef\lr{R} + \else\nomarginoutput{\vbox to\pageheight{\box\sbox\vss + \hbox to\pagewidth{\box\lbox\hfil\page}}}\lheader\rheader + \global\nextindextrue + \global\vsize\pageheight\gdef\lr{L}\global\pagesavedfalse\fi} +\fi +\message{Index:} +\mark{{0}{0}} +\noindent\par +\begingroup + \rightskip 0pt plus 12pt + \parskip 0pt plus .5pt + \hyphenpenalty 10000 \parindent0pt + \small\baselineskip=11pt +\def\item{\par\hangindent 40pt\relax\ifnextindex\mark{{Index}{Index}}\fi}% +\def\subitem{\par\hangindent 40pt\hskip 20pt\relax}% +\def\subsubitem{\par\hangindent 40pt\hskip 30pt\relax}% +\def\indexspace{\par\vskip 12pt plus 6pt minus 4pt\vskip 12pt\goodbreak\vskip -12pt\relax}% +\def\indexheading##1{{\tenbf ##1}\nobreak\vskip 3pt\relax} +} +\def\endindex{% +\endgroup + \vfill\eject % complete the current column. +\ifhint\relax\else + \if R\lr\null\vfill\eject\fi % if necessarry add a right column +\fi +} + + +%% +%% Label page, section, toccount, figure, table, code, enum +%% +\newcount\cdcount +\cdcount=0 + +\newdimen\labelskip +\def\label#1{% writes \labeldef{name}{{page}{section}{toccount}{figure}{table}{code}{enum}} to aux + \ifhint + \global\advance\indexcount by 1\indexdest + \begingroup\@readauxfile + \edef\next{\@writeaux{\string\labeldef{#1}{{\the\indexcount}% + {\thesection}{\the\toccount}{\the\figcount}{\the\tabcount}{\the\cdcount}{\the\enum}}}\endgroup}% + \else + \begingroup\@readauxfile + \edef\next{\@writeaux{\string\labeldef{#1}{{\noexpand\thepageno}% + {\thesection}{\the\toccount}{\the\figcount}{\the\tabcount}{\the\cdcount}{\the\enum}}}\endgroup}% + \fi + \ifvmode %make \removelastskip work after \label + \labelskip=\lastskip + \vskip-\labelskip + \next + \vskip\labelskip + \else + \next + \fi +} + + +\def\m@kelabel#1{label@#1}% this makes the controlseqence from the name + +\def\labeldef#1#2{% #1 is name #2 is page section subsection figure table code +\expandafter\gdef\csname\m@kelabel{#1}\endcsname{#2}}% + +\def\@setref#1#2#3{% + \ifx#1\relax + \message{Undefined reference: #3} + \else + \expandafter#2#1% + \fi} + +\def\@iofvii#1#2#3#4#5#6#7{#1} +\def\@iiofvii#1#2#3#4#5#6#7{#2} +\def\@iiiofvii#1#2#3#4#5#6#7{#3} +\def\@ivofvii#1#2#3#4#5#6#7{#4} +\def\@vofvii#1#2#3#4#5#6#7{#5} +\def\@viofvii#1#2#3#4#5#6#7{#6} +\def\@viiofvii#1#2#3#4#5#6#7{#7} + + +\def\pageref#1{\@readauxfile + \pagelnk{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@iofvii{#1}}} +\def\secref#1{\@readauxfile + \sectionlink + {\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@iiofvii{#1}}% + {\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@iiiofvii{#1}}% +} +\def\figref#1{\@readauxfile +\figlink{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@ivofvii{#1}}} +\def\tabref#1{\@readauxfile +\tablink{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@vofvii{#1}}} +\def\cdref#1{\@readauxfile +\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@viofvii{#1}} +\def\enumref#1{\@readauxfile +\enumlink{\expandafter\@setref\csname\m@kelabel{#1}\endcsname\@viiofvii{#1}}} + +%% +%% Links and Destinations +%% + +\ifbook + \def\pdflinkcolor{0 0 0} % the RGB values for hyperlink color + \def\linkcolor{\Black} + \def\pdflink#1#2{#1} + \def\pdfnote#1.{#1} + \message{No PDF Links} +\else + \def\pdflinkcolor{0 0 1} % the RGB values for hyperlink color + \def\linkcolor{\Blue} + \message{PDF Links are blue} +\fi + +\def\@esphack{\relax\ifhmode\spacefactor\@savsf + {}\ifdim \@savsk >\z@ \ignorespaces + \fi \fi} + +% +% Code destinations: by number +% + +%The number is set be \M and \N and stored as \secno +%The destination is set in \X when we go into C mode +\def\codedest{% +\ifbook\else +\ifhint\HINTdest num \thecode top\relax +\else\ifpdf\pdfdest num \thecode fith\relax +\else\relax +\fi\fi\fi +} +\def\coderef#1{% used in \X +\ifbook${}_{#1}$\else +\ifhint\HINTstartlink goto num #1\lower 1.5pt\hbox{\sevenrm #1}\,\HINTendlink +\else\ifpdf{\eightrm\pdfnote#1.} +\else${}_{#1}$\fi\fi\fi +} + +\def\codenote#1.{% replaces \pdfnote + \setbox0=\hbox{\toksA={#1.}\toksB={}\maketoks}\the\toksA} +% define the following like for pdf +\def\firstsecno#1.{\setbox0=\hbox{\toksA={#1.}\toksB={}% + \def\makenote{\addtokens\toksB{\the\toksC}\def\makenote{\toksD={} + \toksC={}\let\space\empty}\makenote}\maketoks}} +\def\addtokens#1#2{\edef\addtoks{\noexpand#1={\the#1#2}}\addtoks} +\def\poptoks#1#2|ENDTOKS|{\let\first=#1\toksD={#1}% + \ifcat\noexpand\first0\countB=`#1\else\countB=0\fi\toksA={#2}} +\def\maketoks{\expandafter\poptoks\the\toksA|ENDTOKS|% + \ifnum\countB>`9 \countB=0 \fi + \ifnum\countB<`0 + \ifnum0=\countC\else\makenote\fi + \ifx\first.\let\next=\maketoksdone\else + \let\next=\maketoks + \addtokens\toksB{\the\toksD} + \ifx\first,\addtokens\toksB{\space}\fi + \fi + \else \addtokens\toksC{\the\toksD}\global\countC=1\let\next=\maketoks + \fi + \next +} +\def\makenote{\addtokens\toksB + {\noexpand\codelink{\the\toksC}{\romannumeral\the\toksC}}\toksC={}\global\countC=0} +\def\maketoksdone{\edef\st{\global\noexpand\toksA={\the\toksB}}\st} + +\def\codelink#1#2{ +\ifbook#1\else +\ifhint\HINTstartlink goto num #1 #1\HINTendlink +\else\ifpdf\pdflink{#1}{#2}% +\else#1% +\fi\fi\fi} + +% +% new destinations and links: by name +% + +\def\newdest#1{% used to make a new destination +%\message{New destination #1}% +\ifbook +\else\ifpdf + \pdfdest name {#1} fith%\message{Defining pdf label #1}% +\else\ifhint + \HINTdest name {#1}%\message{Defining HINT label #1}% +\fi\fi\fi} + +\def\newlink#1#2{% + \ifbook #2\else + \ifpdf + \pdfstartlink goto name {#1}\linkcolor#2\Black\pdfendlink + \else\ifhint + \HINTstartlink goto name {#1}#2\HINTendlink + \else + #2% + \fi\fi\fi +} + + +%SC.-\the\plaincount for plain sections +%SC.\thesection for regular sections, with subsections and letters for the appendix + +\def\tocref#1#2#3#4{% depth, title, page, label used in tocline + \ifbook#3\else + \ifpdf + \pdfstartlink goto name {SC.#4}\relax \linkcolor#3\Black\pdfendlink + \ifnum#1=0\pdfoutline goto name {SC.#4} {#2}\fi + \else\ifhint + \HINTstartlink goto name {SC.#4}\relax $\rightarrow$\HINTendlink + \HINToutline goto name {SC.#4} depth #1 {#2}% + \else + #3% + \fi\fi\fi +}% + +% +% Page references from the index +% + +% \index{keyword} -> \@wrindex{file}{keyword} writes the nectessary information +% makeindex produces the index and pages are enclosed in \hyperref{pagenumber} +% Problems: +% pagenumbers may be roman numerals in the frontmatter +% hyperpage has to cope with 6 different formats +% \hyperpage{50} a single page number +% \hyperpage{51, 52} two numbers +% \hyperpage{53--59} a page range +% \hyperpage{xi} a roman numeral +% \hyperpage{xi, xii} two numbers +% \hyperpage{xi--xx} a page range + + +% Writing the index file: in HINT replacing the page number by the index count +\newcount\indexcount +\def\theindexcount{\the\indexcount} + +\ifhint + \def\@wrindex#1#2{% file, keyword + \global\advance\indexcount by 1\indexdest + \immediate\write#1{\string + \indexentry{#2:hyperpage}{\the\indexcount}}\endgroup + \if@nobreak \ifvmode\@nobreak\fi\fi\@esphack} + \else + \def\@wrindex#1#2{% + \xdef\gtempa{\write#1{\string + \indexentry{#2:hyperpage}{\noexpand\thepageno}}}\endgroup\gtempa + \if@nobreak \ifvmode\@nobreak\fi\fi\@esphack} +\fi + + +% for HINT and PDF we used hyperrange and hypercomma to split the +% arguments into index/page numbers and then use pagelink +\ifbook + \def\hyperpage#1{#1}\else +\ifhint + \def\hyperpage#1{\hyperrange#1----+\relax} +\else\ifpdf + \def\hyperpage#1{\hyperrange#1----+\relax} +\else + \def\hyperpage#1{#1} +\fi\fi\fi + +%\def\indexlink#1{#1} + +\def\hyperrange#1--#2--#3+{% + \ifx\empty#2\empty% + \hypercomma#1, ,+% + \else\ifhint + \pagelnk{#1}--\pagelink{#2}{$\leftarrow$}% + \else + \pagelnk{#1}--\pagelnk{#2}% + \fi\fi} + +\def\hypercomma#1, #2,#3+{% + \ifx\empty#2\empty% + \pagelnk{#1}% + \else\ifhint% + \pagelink{#1}{$\longrightarrow$}% + \else + \pagelnk{#1}% +% I omit the second page number \ifpdf, \pagelnk{#2}\fi + \fi\fi +} + + +% +% Labels +% + +% section references from \secref use \sectionlink +% for plainsections, we display the label name because there +%is no section number. + +\def\sectionlink#1#2{% \message{Sectionlink #1 : #2}% + \ifbook#1\else + \ifpdf + \pdfstartlink goto name {SC.#2} \linkcolor#1\Black\pdfendlink + \else\ifhint + \HINTstartlink goto name {SC.#2}#1\HINTendlink + \else + #1% + \fi\fi\fi +} + + +% page references from \label and \pageref use \pagelink as before +\def\indexdest{\newdest{page.\the\indexcount}} +\def\pagelnk#1{% + \ifhint + \pagelink{#1}{$\rightarrow$}% + \else + \pagelink{#1}{#1}% + \fi +} +\def\pagelink#1#2{\newlink{page.#1}{#2}} +% For hint the destination is generated with each \index and \label +% Because of the roman numerals, for pdf files +% the pdf destination is generated in the header using \pagelabel + + +\ifbook\let\pagelabel=\relax\else +\ifpdf +\def\pagelabel{%\message{pagelabel=\thepageno} +\pdfdest name {page.\thepageno} fitv\relax} +\else +\let\pagelabel=\relax +\fi\fi + +% references to Figures, Tables, Enumerations +\def\figdest{\newdest{FI.\the\figcount}} +\def\figlink#1{\newlink{FI.#1}{#1}} +\def\tabdest{\newdest{TA.\the\tabcount}} +\def\tablink#1{\newlink{TA.#1}{#1}} +\def\enumdest{\newdest{EN.\the\enum}} +\def\enumlink#1{\newlink{EN.#1}{#1}} + +% +% References to the bibliography +% +\def\citedest#1{\newdest{CI.#1}} +\def\citelink#1{%\message{Citelink #1}% + \newlink{CI.#1}{#1}} + +\def\bblhook{%defined before reading the .bbl file + \def\biblabelprint##1{% + \citedest{##1}% + \noindent + \hbox to \biblabelwidth{% + \biblabelprecontents + \biblabelcontents{##1}% + \biblabelpostcontents + }% + \kern\biblabelextraspace + }% +} +\def\@onecitation#1\@@{%called for each citation + \if@notfirstcitation + \printbetweencitations + \fi + % + \expandafter \ifx \csname\@citelabel{#1}\endcsname \relax + \if@citewarning + \message{\@linenumber Undefined citation `#1'.}% + \fi + % Give it a dummy definition: + \expandafter\gdef\csname\@citelabel{#1}\endcsname{% + {\tt + \escapechar = -1 + \nobreak\hskip0pt + \expandafter\string\csname#1\endcsname + \nobreak\hskip0pt + }% + }% + \fi + % Now produce the text, whether it was undefined or not. + \citelink{\csname\@citelabel{#1}\endcsname}% + \@notfirstcitationtrue +}% +%% +%% Images +%% + +\ifbook +\def\includefig#1{\leavevmode\hbox{\immediate\pdfximage{image/#1.pdf}\pdfrefximage\pdflastximage}} +\else\ifpdf +\def\includefig#1{\leavevmode\hbox{\immediate\pdfximage{image/#1.pdf}\pdfrefximage\pdflastximage}} +\else\ifhint + \def\includefig#1{\HINTimage=image/#1.png\relax} +\else + \input epsf.tex + \def\includefig#1{\epsfbox{image/#1.eps}}% this is a \leavevmode\hbox{...} +\fi\fi\fi + +\ifhint\eject\fi% hitex will remove empty pages that were introduced by cwebmac +\makeatother diff --git a/Build/source/texk/web2c/texprofdir/texprofileman.tex b/Build/source/texk/web2c/texprofdir/texprofileman.tex new file mode 100644 index 00000000000..4a6f5c2e2b0 --- /dev/null +++ b/Build/source/texk/web2c/texprofdir/texprofileman.tex @@ -0,0 +1,442 @@ +% Copyright 2017-2024 Martin Ruckert, Hochschule Muenchen, Lothstrasse 64, 80336 Muenchen +% +% Permission is hereby granted, free of charge, to any person obtaining a copy +% of this software and associated documentation files (the "Software"), to deal +% in the Software without restriction, including without limitation the rights +% to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +% copies of the Software, and to permit persons to whom the Software is +% furnished to do so, subject to the following conditions: +% +% The above copyright notice and this permission notice shall be +% included in all copies or substantial portions of the Software. +% +% THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +% IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +% FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +% COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +% WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT +% OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +% THE SOFTWARE. +% +% Except as contained in this notice, the name of the copyright holders shall +% not be used in advertising or otherwise to promote the sale, use or other +% dealings in this Software without prior written authorization from the +% copyright holders. +\documentclass[a4paper,english]{article} +\usepackage{a4wide} +\usepackage{latex2man} + +\setVersion{1.0} +\setDate{6-9-2024} + +\begin{document} +\begin{Name}{1}{texprofile}{Martin Ruckert}{Displaying TeX profiles}{TeXprofile: Displaying TeX profiles} + + \Prog{texprofile} - Displaying the profile data collected by \Prog{texprof}. +\end{Name} + +\section{Synopsis} + +\Prog{texprofile} [Options] \Arg{inputfile} + +\section{Description} +\Prog{texprofile} reads the binary \Arg{inputfile} as produced by +\Prog{texprof}. The extension \File{.tprof} of the input file can be +omitted. It analyses the data and presents the results in a variety of +tables, either optimized for human readability or as CSV (comma +separated values) files for further processing. + +The output is written to the standard output stream where it can be redirected +to a file or viewed immediately. + +\section{Options} +There are four types of options: general options, table options, +selection options, and formatting options. + +The general options: +\begin{description} +\item[\Opt{-?} \Opt{-h} \Opt{--help}] + Display a short help text and exit the program. +\item[\Opt{--version}] + Display the version information and exit. +\end{description} + +The table options determine the tables that will be displayed. +These options all use upper case letters. +If no table option is given, only some global information is shown. + +\begin{description} +\item[\Opt{-T}] + Show the table of the top 10 input lines. +\item[\Opt{-G}] + Show the table of the macro call graph. +\item[\Opt{-C}] Show the table of times per TeX command. +\item[\Opt{-L}] Show the table of times per input line. +\item[\Opt{-R}] Show the table of raw time stamps. +\item[\Opt{-S}] Show the table of macro stack changes. +\item[\Opt{-F}] Show the table of all input files. +\item[\Opt{-M}] Show the table of all macros called. +\item[\Opt{-A}] Show important tables (equal to -TGFC) tables. +\item[\Opt{-N}] Do not show the table of global information. +\end{description} + +The selection options allow to reduce the amount of data that is shown +in the table by omitting table entries that contribute little to the +overall runtime. + +\begin{description} +\item[\OptArg{-p}{n}] Do not show information for table entries + with cumulative time below \Arg{n} percent. The default is 1.0 percent. +\item[\OptArg{-t}{n}] Limit the number of input lines shown in the + table of the ``Top Ten'' to \Arg{n}. This option is only useful + with the \Opt{-T} option. +\end{description} + +The formatting option determine the formatting of the information in the tables +as well as the selection of information that is shown in the tables. + +\begin{description} +\item[\Opt{-i}] + Add the macros file and line numbers after the macro name. + This option is useful to distinguish two macros that share + the same name. +\item[\Opt{-m}] + Optimize output for machine readability. When optimized for human readability, + times are rounded and given in a short form adding a unit identifier: + s for seconds, ms for milliseconds, us for micro seconds, and ns for + nanoseconds. This format is inconvenient if you want to import the data + for example into a spread-sheet for further processing. + With the \Opt{-m} option times are simply given in nano seconds without units. + Similar the combined time for the total time used for a macro and the + time used as a sub-macro from the call graph is given in a single column + with a slash separating both numbers is nice for human readers. + With the \Opt{-m} option both numbers will simply be given in separate columns. + +\item[\Opt{-n}] + Show the time stamp numbers. This option is useful together with + the \Opt{-R} option, if you import the table into a database or spread-sheet. + If you sort the data by various properties into different + categories, the time stamp number can be used to keeping the entries + within a category sorted by time. + +\item[\Opt{-s}] + Show the changes of the macro stack. + This option is useful with the \Opt{-R} option if you want to see the changes + in the macro stack (see \Opt{-S}) together with the timing information in + a single table. +\end{description} + + +\section{Data Formats} +\subsection{File Numbers} +While \Prog{texprof} is running, every TeX input file is given a +unique file number. The file name alone is often not unique because +two files in different directories might have the same file +name. Displaying the full file name with the complete path is however often +not very +convenient. So if you observe the same file name together with +different file numbers, you can use the option \Opt{-F} to get a table of all +file numbers and their full file names. + +But even within the same +directory, TeX can read different files with the same name during one +run: TeX can open a file for writing, write content to the file, close +it, open it for reading, read it and close it again. Then TeX might +repeat this process a second time, or multiple times, reusing the same +file name over and over again. \Prog{texprof} will assign a new file +number to this file each time it is opened for reading. So you can +tell from the sequence of file numbers which one was the first, the +second, or the third. + +Last not least, there are some special file +numbers: +\begin{description} +\item[0 - unknown] + If the file is unknown, which should rarely happen, + the file number 0 is used. +\item[1- system] + \Prog{texprof} will map + time intervals that are used to execute certain system routines + to the ``system'' pseudo file +using the line numbers to identify the specific routine +like producing the output DVI file (shipout), breaking +a paragraph into lines (linebrk), or breaking the +document into pages (buildpg). These times do not depend on the +use of macros but simply on the size of the document. +\item[2 - terminal] + TeX commands entered on the command line or interactively are mapped + to file number 2. +\end{description} + +\subsection{Time} +If the option \Opt{-m} is given times are given as nanoseconds. +Otherwise, times are rounded to at most 4 digits precission and +displayed with a unit: seconds (s), milliseconds (ms), microseconds +(um), or nanoseconds (ns). + +\subsection{Macro Names} +Macro names are shown with the leading backslash. Since macro names +are often not unique, the command line option \Opt{-i} can be used to +show after the macro name in square brackets the file number and the +line number where the macro is defined. Unless two macros with the +same name are defined in the same file and line this is sufficient to +uniquely identify a macro. A macro defined with ``let'' results in a +complet copy of the original macro. Therefore it will not reference +the file and line of the ``let'' command but the same file and line as +the original macro. + +\section{Examples} +Let's assume that you issue the command \Prog{texprof} \Opt{-prof} +\Arg{hello.tex}. This will run \Prog{texprof} on the input file +\Arg{hello.tex} with option \Opt{-prof}. The program \Prog{texprof} +will load the plain TeX format and then process \File{hello.tex} to +produce \File{hello.log} and \File{hello.dvi}. It will execute +exactly the same steps that TeX would execute if you had issued the +command \Prog{tex} \Arg{hello.tex}. + +In addition to \File{hello.log} and \File{hello.dvi}, \Prog{texprof} +will also produce the file \File{hello.tprof} containing time +measurements made while \Prog{texprof} was running. The option +\Opt{-prof} will switch on the gathering of timing data as soon as +\Prog{texprof} enters its main control procedure; The file +\File{hello.tprof} will contain a time measurement, called a time +stamp, for every command that TeX executed while processing the input. +A binary format is used to store all that data in a simple and compact +form. Still the file \File{hello.tprof} might become very big. + +The program \Prog{texprofile} is used to extract and analyse the data +contained in \File{hello.tprof} and produce useful output. Here are +some examples: + +\begin{description} +\item[\Prog{texprofile} \Arg{hello}] + Without further options \Prog{texprofile} will write some general + information to the standard output, like the total time measured, + the number of samples, the average time per sample, etc. + The general information is always given unless explicitely disabled + with the \Opt{-N} option or with the \Opt{-m} option. + +\item[\Prog{texprofile} \Opt{-T} \Arg{hello}] + With the option \Opt{-T}, \Prog{texprofile} will map each time stamp + to a specific line of input, add up the time intervalls for each input + line separately, and output a table showing the ten lines that have + the highest cummulative time. + The table has the following seven columns: + \begin{description} + \item[1. file] + The first column contains the input file number as explained above. + The input file name if shown in column 7. + \item[2. line] + The line number. + \item[3. percent] + The time spent in this line and file as a percentage of the total + time measured as given as part of the general information. + \item[4. absolute] + The absolute time spent in this line and file. + \item[5. count] + The number of times the execution path entered this line. + Note, that a + macro call usually redirects the execution path to another line + from where the execution will return after the macro call has completed. + Reentering the line after such an excursion to an other line will + cause this counter to be incremented. In summary, this counter might + reflect the number of partial executions of a line not the number + of full executions of the entire line. + \item[6. average] + The average time spent in the line is simply computed by dividing the + value in column 4 by the value in column 5. + \item[7. file] + The input file name. The corresponding file number is shown in column 1. + \end{description} + +\item[\Prog{texprofile} \Opt{-G} \Arg{hello}] + With the option \Opt{-G}, \Prog{texprofile} will map each time stamp + either to file input or to a macro body. + The table shown is divided into several sections, the first section + is devoted to file input, each of the following sections is devoted + to a specific macro. + \\ + Each section starts with a header. The header of the + first section is ``File'' the header of a later section is the macro name. + \\ + The first line after the header gives the total time spent in the section + in two different formats: + \begin{description} + \item[1. time] + Column 1 gives it as an absolut time. + \item[3. percentage] + Column 3 gives it as a percentage of the total time measured. + \end{description} + For the first section with the header ``File'', the absolute time + will be equal to the total time measured because \Prog{texprof} + did spent all the time processing the file \Arg{hello}. + And consequently the value in column 3 will be 100\%. + \\ + To accomplish a task, a macro usually calls other + macros that we call child macros in the following. + The following lines in the table will give a breakdown + of the time shown in the first line. + The breakdown starts with a line showing the time spent in the section + excluding the time spent in child macros. It shows in column 4 the number + of times the macro was called. + \begin{description} + \item[1. time] + Column 1 gives the absolut time spent in the section excluding the time + spent in child macros + \item[3. percentage] + Column 3 gives the time from column 1 as a percentage of the total time + spent in this section as given in the previous line. + \item[4. count/total] + Column 4 gives the number of times the section was called. + \end{description} + The lines that follow in the table show the time spent in one of + the child macros. Column 2, 4, and 5 need some explanation. + \begin{description} + \item[2. loop] + The only case where column 2 is not empty is the case of a + recursive macro. A recursive macro is a macro that along the + chain of macro calls eventually calls itself creating a + recursive loop. At this point, a macro becomesits own descendant + and at the same time its own ancestor. + \\ + Therefore \Prog{texprofile} maintains for each child macro two + accumulators for the elapsed time: For the time shown in + column 2 labeled ``loop'', \Prog{texprofile} adds up the time + differences observed at the return of a child macro. For the + time shown in the column labeled ``time'' and ``percent'', it + subtracts from the time differences observed at the return of a + child macro all those time differences that were already added + to one of the other lines in the time breakdown: the macro itself + or one of the other child macros. + So the times shown in column 1 of line 2 and the following lines + will add up to the time shown in column 1 of the line 1; + and the percentages shown in column 3 of line 2 and the following lines + will add up to 100\%. + The time shown in column 3 will show show the total time needed + to accomplish the sub task assigned to the respective child macro. + \item[4. count/total] + Column 4 shows two counts $n/m$ for the macro named in column 5. + $m$ is the total number of calls to the macro and $n$ is the number of + calls as a child macro in the current section. The number $n$ will + always be less or equal to $m$. + + \item[5. child] + Column 5 shows the name of the child macro as explained above. + \end{description} +\item[\Prog{texprofile} \Opt{-G} \Opt{-m} \Arg{hello}] + This table will contain the same data as the previous example + but this time the \Opt{-m} option will optimize the output for + machine readability. + \begin{itemize} + \item There are no column headers. + \item The times in column 1 ``time'' and column 2 ``loop'' + are given in nanoseconds without a unit identifier. + \item The numbers $n/m$ in column 4 are now shown in two separate + columns 4 and 5; the macro name moves from column 5 to column 6. + \end{itemize} + +\item[\Prog{texprofile} \Opt{-F} \Arg{hello}] + With the option \Opt{-F}, \Prog{texprofile} outputs the table + of all of TeX's input files using 5 columns: + \begin{description} + \item[1. file] The file number as explained above. + \item[2. lines] The difference between the highest and the lowest + line number found in the profile data for this file. This is + usually only a subset of all the lines of the file. + \item[3. percent] + The percentage of the total time measured that is attibuted to the file. + \item[4. time] + The absolute time that is attibuted to the file. + \item[5. name] + The full file name of the file. + \end{description} +\item[\Prog{texprofile} \Opt{-C} \Arg{hello}] + With the option \Opt{-C}, \Prog{texprofile} outputs the table + of all TeX commands executed while profiling + using 6 columns: + \begin{description} + \item[1. cmd] The command code used internaly by TeX. + Usually the same number is used by TeX for several + closely related tasks. + There is a special command code 101 which is used to + for the time that is spend on system routines that are + mapped to the system file as explained above. + \item[2. time] + The total time used for the command. + \item[3. percent] + The percentage of the total time measured used for the command. + \item[4. count] + The number of times this command was executed. + \item[5. average] + The average time needed to execute the command. + This is simpy the value in column 2 divided by the value in column 4. + \item[6. name] + A verbal description of the command or commands that share this + command code. + \end{description} +\item[\Prog{texprofile} \Opt{-R} \Opt{-m} \Opt{-n} \Arg{hello}] + With the options \Opt{-R} \Opt{-m}, \Prog{texprofile} outputs the table + of raw time measurements as observed by \Prog{texprof} optimized for machine + readability. The option \Opt{-n} adds a column for the number + of each time measurements. + Because of the \Opt{-m} option, the table has no column headers. + The table has 7 columns: + \begin{description} + \item[1. number] The number of the time measurment. + These numbers are strictly increasing but not necessarily consecutive. + They can be used to keep the measurements sorted in the order in which + they were made. + \item[2. file] The file number as explained above. + \item[3. line] The line number as explained above. + \item[4. time] The time interval in nano seconds. + \item[5. command] The command name as given in column 6 of the previous example. + \item[6. level ] The nesting level of the macro call stack. + \item[7. macro] The macro name. + \end{description} + This table contains all the timing information gathered during the run + of \Prog{texprof}. Some information about macro calls is contained in + column 6. More information about macro calls could be added + by using the \Opt{-s} option. The information about macro returns + can be obtained from column 6. + The table can be imported to a spread-sheet or a database for further + analysis. +\end{description} + + +\section{Bugs} +If the last command in a macro body is a macro call, we call this a tail call. +If such a tail call reads ahead to scan the following input +for possible arguments, the look-ahead mechaism of TeX might push further +macros or new input files on TeX's input stack. +These entries will remain on top of TeXprof's macro nesting stack, +even if TeX backs up all these tokens on its input stack. +This can cause an attribution of runtime to those entries as sub entries +of the tail call. If this explanation sounds complicated to you, it is +because the situation is indeed complicated. + +\section{See Also} + \Cmd{texprof}{1} + + +\section{Version} +Version: \Version\ of \Date +\begin{description} +\item[Copyright] \copyright\ 2024, Martin Ruckert,\\ + Hochschule München, Lothstrasse 64, D-80335 München + +\item[Distribution] + \Prog{texprofile} is distributed with TeX Live. + More recent versions might be found on GitHub + \URL{https://github.com/ruckertm/HINT}. + +\item[License] This program can be redistributed and/or modified under the + terms of the MIT/X11 license. +\end{description} + +\section{Author} +\noindent +Martin Ruckert \\ +Munich University of Applied Sciences \\ +Email: \Email{martin.ruckert@hm.edu} \\ + +\end{document} diff --git a/Build/source/texk/web2c/tftopl.ch b/Build/source/texk/web2c/tftopl.ch index 445ba8e3ab1..c26137b6958 100644 --- a/Build/source/texk/web2c/tftopl.ch +++ b/Build/source/texk/web2c/tftopl.ch @@ -15,6 +15,12 @@ % 01/16/90 (SR) Version 3.1. % (more recent changes in the ChangeLog) +@x [0] l.25 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.28 \def\title{TF\lowercase{to}PL} @y @@ -64,7 +70,7 @@ label @<Labels in the outer block@>@/ procedure initialize; {this procedure gets things started properly} begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} begin kpse_set_program_name (argv[0], my_name); @@ -186,6 +192,18 @@ strcat (ASCII_all, '`abcdefghijklmnopqrstuvwxyz{|}~');@/ MBL_string:=' MBL'; RI_string:=' RI '; RCE_string:=' RCE'; @z +@x [36] l.680 +while l>0 do @<Reduce \(1)|l| by one, preserving the invariants@>; +@y +while l>0 do @<Reduce \(|l| by one, preserving the invariants@>; +@z + +@x [37] l.687 +@ @<Reduce \(1)|l|...@>= +@y +@ @<Reduce \(|l|...@>= +@z + % [38] How we output the character code depends on |charcode_format|. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @x @@ -228,6 +246,18 @@ else begin tfm[0]:=c; out_octal(0,1); put_byte(RCE_string[1+(b div 3)], pl_file); @z +@x [40] l.751 +if a>@'3777 then @<Reduce \(2)negative to positive@>; +@y +if a>@'3777 then @<Reduce negative to positive@>; +@z + +@x [43] l.775 +@ @<Reduce \(2)negative to positive@>= +@y +@ @<Reduce negative to positive@>= +@z + % [78] No progress reports unless verbose. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @x @@ -338,7 +368,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 4; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/tiedir/ChangeLog b/Build/source/texk/web2c/tiedir/ChangeLog index c584f11eef0..b3a98e85a86 100644 --- a/Build/source/texk/web2c/tiedir/ChangeLog +++ b/Build/source/texk/web2c/tiedir/ChangeLog @@ -1,3 +1,7 @@ +2023-09-01 Andreas Scherer <https://ascherer.github.io> + + * tie-w2c.ch: Fix sorting order of names of sections. + 2023-09-03 TANAKA Takuji <ttk@t-lab.opal.ne.jp> * tie.test: Make easier to test on Windows. diff --git a/Build/source/texk/web2c/tiedir/tie-w2c.ch b/Build/source/texk/web2c/tiedir/tie-w2c.ch index fb09e05060f..d29e3f77967 100644 --- a/Build/source/texk/web2c/tiedir/tie-w2c.ch +++ b/Build/source/texk/web2c/tiedir/tie-w2c.ch @@ -252,6 +252,12 @@ replacement part of a change file, or in an incomplerte check if the replacement part of a change file, or in an incomplete check if the @z +@x l.663 + @<Check |c| for |EOF|, |return| if line was empty, otherwise +@y + @<Check \9{c}|c| for |EOF|, |return| if line was empty, otherwise +@z + @x l.667 if (c!=@' ' && c!=tab_mark) @y @@ -272,6 +278,12 @@ final line. Of the current line is empty, we just can \&{return}. final line. If the current line is empty, we just can \&{return}. @z +@x l.696 +@<Check |c| for |EOF|...@>= +@y +@<Check \9{c}|c| for |EOF|...@>= +@z + Section 31 @x l.742 -- print errors on 'stderr' diff --git a/Build/source/texk/web2c/uptexdir/ChangeLog b/Build/source/texk/web2c/uptexdir/ChangeLog index a68dbcce2f4..30a38016535 100644 --- a/Build/source/texk/web2c/uptexdir/ChangeLog +++ b/Build/source/texk/web2c/uptexdir/ChangeLog @@ -1,3 +1,33 @@ +2024-09-30 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * uptex-m.ch: kcatcode (latin_ucs(14)) is removed + since it will be supported in upTeX-2.00. + +2024-09-28 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * uptex-m.ch, upbibtex.ch, kanji.c: + Update reference of Unicode blocks to + "Blocks-16.0.0.txt Date: 2024-02-22". + Also add a new block + "CJK Unified Ideographs Extension J" + which is approved in Unicode 17.0, 2025. + * tests/unibib.{tex,bib,bbl}: Update a test. + +2024-09-22 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * ptex-base.ch, ptex_version.h: Version p4.1.2. + +2024-09-21 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * ptex-base.ch, uptex-m.ch: + Fix a bug in initialization of inhibit_xsp/kinsoku tables + (Bug introduced on commit 2022-12-12 r65248). + +2024-09-16 TANAKA Takuji <ttk@t-lab.opal.ne.jp> + + * ptex-base.ch: Use constants enc_jis and enc_ucs. + * am/uptex.am: Tweak. + 2024-05-18 Hironori Kitagawa <h_kitagawa2001@yahoo.co.jp> * ptex-base.ch: Enable to get character codes diff --git a/Build/source/texk/web2c/uptexdir/am/uptex.am b/Build/source/texk/web2c/uptexdir/am/uptex.am index 1a4ee00d4c0..bf71b77f5c7 100644 --- a/Build/source/texk/web2c/uptexdir/am/uptex.am +++ b/Build/source/texk/web2c/uptexdir/am/uptex.am @@ -263,12 +263,12 @@ DISTCLEANFILES += uptests/ygk256*.* DISTCLEANFILES += uptests/xskipjfmp.* ## uptexdir/uptriptest EXTRA_DIST += uptexdir/uptrip/uptrip.diffs uptexdir/uptrip/texmf.cnf +DISTCLEANFILES += uptrip.diffs + uptrip.diffs: uptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) $(triptrap_diffs) $@ .PHONY: uptrip-clean uptrip-clean: rm -rf uptripdir -DISTCLEANFILES += uptrip.diffs - # (end of uptex.am) diff --git a/Build/source/texk/web2c/uptexdir/kanji.c b/Build/source/texk/web2c/uptexdir/kanji.c index ddeca5a535c..1dca2a340e4 100644 --- a/Build/source/texk/web2c/uptexdir/kanji.c +++ b/Build/source/texk/web2c/uptexdir/kanji.c @@ -91,8 +91,8 @@ integer ktoken_to_chr(integer c) } /* Ref. http://www.unicode.org/Public/UNIDATA/Blocks.txt */ -/* # Blocks-15.1.0.txt */ -/* # Date: 2023-07-28, 15:47:20 GMT */ +/* # Blocks-16.0.0.txt */ +/* # Date: 2024-02-22 */ static long ucs_range[]={ 0x0000, /* Basic Latin */ /* 0x00 */ 0x0080, /* Latin-1 Supplement */ @@ -279,14 +279,15 @@ static long ucs_range[]={ 0x10500, /* Elbasan */ 0x10530, /* Caucasian Albanian */ 0x10570, /* Vithkuqi */ + 0x105C0, /* Todhri */ 0x10600, /* Linear A */ 0x10780, /* Latin Extended-F */ 0x10800, /* Cypriot Syllabary */ 0x10840, /* Imperial Aramaic */ 0x10860, /* Palmyrene */ 0x10880, /* Nabataean */ - 0x108E0, /* Hatran */ - 0x10900, /* Phoenician */ /* 0xc0 */ + 0x108E0, /* Hatran */ /* 0xc0 */ + 0x10900, /* Phoenician */ 0x10920, /* Lydian */ 0x10980, /* Meroitic Hieroglyphs */ 0x109A0, /* Meroitic Cursive */ @@ -301,8 +302,9 @@ static long ucs_range[]={ 0x10C00, /* Old Turkic */ 0x10C80, /* Old Hungarian */ 0x10D00, /* Hanifi Rohingya */ + 0x10D40, /* Garay */ /* 0xd0 */ 0x10E60, /* Rumi Numeral Symbols */ - 0x10E80, /* Yezidi */ /* 0xd0 */ + 0x10E80, /* Yezidi */ 0x10EC0, /* Arabic Extended-C */ 0x10F00, /* Old Sogdian */ 0x10F30, /* Sogdian */ @@ -316,26 +318,29 @@ static long ucs_range[]={ 0x11150, /* Mahajani */ 0x11180, /* Sharada */ 0x111E0, /* Sinhala Archaic Numbers */ - 0x11200, /* Khojki */ + 0x11200, /* Khojki */ /* 0xe0 */ 0x11280, /* Multani */ - 0x112B0, /* Khudawadi */ /* 0xe0 */ + 0x112B0, /* Khudawadi */ 0x11300, /* Grantha */ + 0x11380, /* Tulu-Tigalaria */ 0x11400, /* Newa */ 0x11480, /* Tirhuta */ 0x11580, /* Siddham */ 0x11600, /* Modi */ 0x11660, /* Mongolian Supplement */ 0x11680, /* Takri */ + 0x116D0, /* Myanmar Extended-C */ 0x11700, /* Ahom */ 0x11800, /* Dogra */ 0x118A0, /* Warang Citi */ 0x11900, /* Dives Akuru */ - 0x119A0, /* Nandinagari */ + 0x119A0, /* Nandinagari */ /* 0xf0 */ 0x11A00, /* Zanabazar Square */ 0x11A50, /* Soyombo */ 0x11AB0, /* Unified Canadian Aboriginal Syllabics Extended-A */ - 0x11AC0, /* Pau Cin Hau */ /* 0xf0 */ + 0x11AC0, /* Pau Cin Hau */ 0x11B00, /* Devanagari Extended-A */ + 0x11BC0, /* Sunuwar */ 0x11C00, /* Bhaiksuki */ 0x11C70, /* Marchen */ 0x11D00, /* Masaram Gondi */ @@ -345,20 +350,23 @@ static long ucs_range[]={ 0x11FB0, /* Lisu Supplement */ 0x11FC0, /* Tamil Supplement */ 0x12000, /* Cuneiform */ - 0x12400, /* Cuneiform Numbers and Punctuation */ + 0x12400, /* Cuneiform Numbers and Punctuation */ /* 0x100 */ 0x12480, /* Early Dynastic Cuneiform */ 0x12F90, /* Cypro-Minoan */ 0x13000, /* Egyptian Hieroglyphs */ 0x13430, /* Egyptian Hieroglyph Format Controls */ - 0x14400, /* Anatolian Hieroglyphs */ /* 0x100 */ + 0x13460, /* Egyptian Hieroglyphs Extended-A */ + 0x14400, /* Anatolian Hieroglyphs */ + 0x16100, /* Gurung Khema */ 0x16800, /* Bamum Supplement */ 0x16A40, /* Mro */ 0x16A70, /* Tangsa */ 0x16AD0, /* Bassa Vah */ 0x16B00, /* Pahawh Hmong */ + 0x16D40, /* Kirat Rai */ 0x16E40, /* Medefaidrin */ 0x16F00, /* Miao */ - 0x16FE0, /* Ideographic Symbols and Punctuation */ + 0x16FE0, /* Ideographic Symbols and Punctuation */ /* 0x110 */ 0x17000, /* Tangut */ 0x18800, /* Tangut Components */ 0x18B00, /* Khitan Small Script */ @@ -366,14 +374,15 @@ static long ucs_range[]={ 0x1AFF0, /* Kana Extended-B */ 0x1B000, /* Kana Supplement */ 0x1B100, /* Kana Extended-A */ - 0x1B130, /* Small Kana Extension */ /* 0x110 */ + 0x1B130, /* Small Kana Extension */ 0x1B170, /* Nushu */ 0x1BC00, /* Duployan */ 0x1BCA0, /* Shorthand Format Controls */ + 0x1CC00, /* Symbols for Legacy Computing Supplement */ 0x1CF00, /* Znamenny Musical Notation */ 0x1D000, /* Byzantine Musical Symbols */ 0x1D100, /* Musical Symbols */ - 0x1D200, /* Ancient Greek Musical Notation */ + 0x1D200, /* Ancient Greek Musical Notation */ /* 0x120 */ 0x1D2C0, /* Kaktovik Numerals */ 0x1D2E0, /* Mayan Numerals */ 0x1D300, /* Tai Xuan Jing Symbols */ @@ -382,13 +391,14 @@ static long ucs_range[]={ 0x1D800, /* Sutton SignWriting */ 0x1DF00, /* Latin Extended-G */ 0x1E000, /* Glagolitic Supplement */ - 0x1E030, /* Cyrillic Extended-D */ /* 0x120 */ + 0x1E030, /* Cyrillic Extended-D */ 0x1E100, /* Nyiakeng Puachue Hmong */ 0x1E290, /* Toto */ 0x1E2C0, /* Wancho */ 0x1E4D0, /* Nag Mundari */ + 0x1E5D0, /* Ol Onal */ 0x1E7E0, /* Ethiopic Extended-B */ - 0x1E800, /* Mende Kikakui */ + 0x1E800, /* Mende Kikakui */ /* 0x130 */ 0x1E900, /* Adlam */ 0x1EC70, /* Indic Siyaq Numbers */ 0x1ED00, /* Ottoman Siyaq Numbers */ @@ -398,13 +408,13 @@ static long ucs_range[]={ 0x1F0A0, /* Playing Cards */ 0x1F100, /* Enclosed Alphanumeric Supplement */ 0x1F200, /* Enclosed Ideographic Supplement */ - 0x1F300, /* Miscellaneous Symbols and Pictographs */ /* 0x130 */ + 0x1F300, /* Miscellaneous Symbols and Pictographs */ 0x1F600, /* Emoticons */ 0x1F650, /* Ornamental Dingbats */ 0x1F680, /* Transport and Map Symbols */ 0x1F700, /* Alchemical Symbols */ 0x1F780, /* Geometric Shapes Extended */ - 0x1F800, /* Supplemental Arrows-C */ + 0x1F800, /* Supplemental Arrows-C */ /* 0x140 */ 0x1F900, /* Supplemental Symbols and Pictographs */ 0x1FA00, /* Chess Symbols */ 0x1FA70, /* Symbols and Pictographs Extended-A */ @@ -414,12 +424,13 @@ static long ucs_range[]={ 0x2B740, /* CJK Unified Ideographs Extension D */ 0x2B820, /* CJK Unified Ideographs Extension E */ 0x2CEB0, /* CJK Unified Ideographs Extension F */ - 0x2EBF0, /* CJK Unified Ideographs Extension I */ /* 0x140 */ + 0x2EBF0, /* CJK Unified Ideographs Extension I */ 0x2F800, /* CJK Compatibility Ideographs Supplement */ 0x30000, /* CJK Unified Ideographs Extension G */ 0x31350, /* CJK Unified Ideographs Extension H */ - 0x323B0, /* reserved */ - 0x40000, /* reserved */ + 0x323B0, /* CJK Unified Ideographs Extension J (Unicode 17.0) */ + 0x33480, /* reserved */ + 0x40000, /* reserved */ /* 0x150 */ 0x50000, /* reserved */ 0x60000, /* reserved */ 0x70000, /* reserved */ @@ -430,14 +441,14 @@ static long ucs_range[]={ 0xC0000, /* reserved */ 0xD0000, /* reserved */ 0xE0000, /* Tags */ - 0xE0100, /* Variation Selectors Supplement */ /* 0x150 */ + 0xE0100, /* Variation Selectors Supplement */ 0xE01F0, /* reserved */ 0xF0000, /* Supplementary Private Use Area-A */ 0x100000, /* Supplementary Private Use Area-B */ /* Value over 0x10FFFF is illegal under Unicode, - They are for some special use. *** experimental *** */ + We use for some special application. */ 0x110000, /* Reserved */ - 0x120000, /* Reserved */ + 0x120000, /* Reserved */ /* 0x160 */ 0x130000, /* Reserved */ 0x140000, /* Reserved */ 0x150000, /* Reserved */ @@ -448,20 +459,20 @@ static long ucs_range[]={ 0x1A0000, /* Reserved */ 0x1B0000, /* Reserved */ 0x1C0000, /* Reserved */ - 0x1D0000, /* Reserved */ /* 0x160 */ + 0x1D0000, /* Reserved */ 0x1E0000, /* Reserved */ 0x1F0000, /* Reserved */ 0x200000, /* Reserved */ 0x210000, /* Reserved */ - 0x220000, /* Kana with Voiced Sound Mark */ + 0x220000, /* Kana with Voiced Sound Mark */ /* 0x170 */ 0x240000, /* Kana with Semi-Voiced Sound Mark */ 0x25E6E6, /* Emoji Flag Sequence */ 0x260000, /* Emoji with Modifier Fitzpatrick */ 0x300000, /* Reserved */ 0x400000, /* Standardized Variation Sequence */ 0x800000, /* Emoji Keycap Sequence */ - 0x800080, /* Ideographic Variation Sequence */ /* 0x16C */ - CJK_CHAR_LIMIT, /* Ideographic Variation Sequence, VS49..VS256 */ + 0x800080, /* Ideographic Variation Sequence, VS17..VS48 */ + CJK_CHAR_LIMIT, /* Ideographic Variation Sequence, VS49..VS256 */ /* 0x178 */ IVS_CHAR_LIMIT }; @@ -544,11 +555,11 @@ integer kcatcodekey(integer c) if ( COMBINING_ENCLOSING_KEYCAP==c ) return 0x1FA; break; - case 0x12e: /* Block : Enclosed Alphanumeric Supplement */ + case 0x138: /* Block : Enclosed Alphanumeric Supplement */ if ( REGIONAL_INDICATOR_SYMBOL_LETTER_A <=c && c<= REGIONAL_INDICATOR_SYMBOL_LETTER_Z ) return 0x1FB; break; - case 0x130: /* Block : Miscellaneous Symbols and Pictographs */ + case 0x13a: /* Block : Miscellaneous Symbols and Pictographs */ if ( EMOJI_MODIFIER_FITZPATRIC_TYPE1_2 <=c && c<= EMOJI_MODIFIER_FITZPATRIC_TYPE6 ) return 0x1FC; break; diff --git a/Build/source/texk/web2c/uptexdir/ptex-base.ch b/Build/source/texk/web2c/uptexdir/ptex-base.ch index 6b77387d58e..36496b2e4b2 100644 --- a/Build/source/texk/web2c/uptexdir/ptex-base.ch +++ b/Build/source/texk/web2c/uptexdir/ptex-base.ch @@ -74,6 +74,8 @@ % (2022-10-24) HY pTeX p4.1.0 Add new syntax \font [in jis/ucs]. % New primitives: \tojis, \ptextracingfonts and \ptexfontname. % (2023-09-17) HY pTeX p4.1.1 Support more than 256 different glue/kern. +% (2024-09-22) HK pTeX p4.1.2 Enable to get character codes +% from control symbols by using backquote. @x % Here is TeX material that gets inserted after \input webmac @@ -88,8 +90,8 @@ @y @d pTeX_version=4 @d pTeX_minor_version=1 -@d pTeX_revision==".1" -@d pTeX_version_string=='-p4.1.1' {current \pTeX\ version} +@d pTeX_revision==".2" +@d pTeX_version_string=='-p4.1.2' {current \pTeX\ version} @# @d pTeX_banner=='This is pTeX, Version 3.141592653',pTeX_version_string @d pTeX_banner_k==pTeX_banner @@ -1305,10 +1307,11 @@ for k:=0 to 255 do begin cat_code(k):=other_char; kcat_code(k):=other_kchar; math_code(k):=hi(k); sf_code(k):=1000; auto_xsp_code(k):=0; + inhibit_xsp_type(k):=0; kinsoku_type(k):=0; end; for k:=0 to 1023 do - begin inhibit_xsp_code(k):=0; inhibit_xsp_type(k):=0; - kinsoku_code(k):=0; kinsoku_type(k):=0; + begin inhibit_xsp_code(k):=0; + kinsoku_code(k):=0; end; @z @x [17.232] l.4971 - pTeX: initialize cat_code, cur_jfont, cur_tfont @@ -1523,6 +1526,9 @@ primitive("ptextracingfonts",assign_int,int_base+ptex_tracing_fonts_code);@/ @d v_offset==dimen_par(v_offset_code) @d t_baseline_shift==dimen_par(t_baseline_shift_code) @d y_baseline_shift==dimen_par(y_baseline_shift_code) +@# +@d enc_jis=1 +@d enc_ucs=2 @z @x l.5542 - pTeX: @@ -1660,8 +1666,8 @@ procedure print_font_dir_and_enc(f:internal_font_number); begin if font_dir[f]=dir_tate then print("/TATE") else if font_dir[f]=dir_yoko then print("/YOKO"); - if font_enc[f]=2 then print("+Unicode") - else if font_enc[f]=1 then print("+JIS"); + if font_enc[f]=enc_ucs then print("+Unicode") + else if font_enc[f]=enc_jis then print("+JIS"); end; @z @@ -3324,9 +3330,9 @@ if jfm_flag<>dir_default then for k:=ctype_base[f] to ctype_base[f]+nt-1 do begin fget; read_twentyfourx(cx); - if jfm_enc=2 then {Unicode TFM} + if jfm_enc=enc_ucs then {Unicode TFM} font_info[k].hh.rh:=toDVI(fromUCS(cx)) - else if jfm_enc=1 then {JIS-encoded TFM} + else if jfm_enc=enc_jis then {JIS-encoded TFM} font_info[k].hh.rh:=toDVI(fromJIS(cx)) else font_info[k].hh.rh:=tokanji(cx); {|kchar_code|} @@ -3584,9 +3590,9 @@ continue: end; p:=link(p); jc:=KANJI(info(p)); - if font_enc[f]=2 then {Unicode TFM} + if font_enc[f]=enc_ucs then {Unicode TFM} jc:=toUCS(jc) - else if font_enc[f]=1 then {JIS-encoded TFM} + else if font_enc[f]=enc_jis then {JIS-encoded TFM} begin if toJIS(jc)=0 then char_warning_jis(f,jc); jc:=toJIS(jc); end else @@ -7075,8 +7081,8 @@ exit:end; @ @<Scan the font encoding specification@>= begin jfm_enc:=0; if scan_keyword_noexpand("in") then - if scan_keyword_noexpand("jis") then jfm_enc:=1 - else if scan_keyword_noexpand("ucs") then jfm_enc:=2 + if scan_keyword_noexpand("jis") then jfm_enc:=enc_jis + else if scan_keyword_noexpand("ucs") then jfm_enc:=enc_ucs else begin print_err("Unknown TFM encoding"); @.Unknown TFM encoding@> diff --git a/Build/source/texk/web2c/uptexdir/ptex_version.h b/Build/source/texk/web2c/uptexdir/ptex_version.h index 13e0f1467b3..247aabb113d 100644 --- a/Build/source/texk/web2c/uptexdir/ptex_version.h +++ b/Build/source/texk/web2c/uptexdir/ptex_version.h @@ -1 +1 @@ -#define PTEX_VERSION "p4.1.1" +#define PTEX_VERSION "p4.1.2" diff --git a/Build/source/texk/web2c/uptexdir/tests/unibib.bbl b/Build/source/texk/web2c/uptexdir/tests/unibib.bbl index e61fc1c3474..b65e9ec58b9 100644 --- a/Build/source/texk/web2c/uptexdir/tests/unibib.bbl +++ b/Build/source/texk/web2c/uptexdir/tests/unibib.bbl @@ -1,4 +1,4 @@ -\begin{thebibliography}{1} +\begin{thebibliography}{10} \bibitem{björk2020} Tomas Björk. @@ -22,6 +22,11 @@ Dustin Boswell and Trevor Foucher. \newblock 구운몽. \newblock 高麗書林, 1975. +\bibitem{imahashi} +𫝆𫞎姬, 𡈽𪧦, 𠮷田真. +\newblock CJK統合漢字拡張🄑--🄓のテスト --- その🄰. +\newblock 冬𠘨𫝷, 2024. + \bibitem{nikos} Νίκος Καζαντζάκης. \newblock {\em Συμπόσιον}. diff --git a/Build/source/texk/web2c/uptexdir/tests/unibib.bib b/Build/source/texk/web2c/uptexdir/tests/unibib.bib index 11e6baa22a8..d4c45d08e12 100644 --- a/Build/source/texk/web2c/uptexdir/tests/unibib.bib +++ b/Build/source/texk/web2c/uptexdir/tests/unibib.bib @@ -79,3 +79,25 @@ publisher = "Snowman commedian Press", year = 2020, } + +%% CJK統合漢字拡張B-Dのテスト +@book{imahashi, + author = "𫝆𫞎 姬 and 𡈽 𪧦 and 𠮷田 真", + yomi = "Hime Imahashi", + title = "CJK統合漢字拡張🄑--🄓のテスト --- その🄰", + publisher = "冬𠘨𫝷", + year = 2024, +} +% 𫝆 U+2B746 CJK Unified Ideographs Extension D AJ:13780 +% 𫞎 U+2B78E CJK Unified Ideographs Extension D AJ:13724 +% 姬 U+2F862 CJK Compatibility Ideographs Supplement AJ:13998 +% 𡈽 U+2123D CJK Unified Ideographs Extension B AJ:13953 J3-2F42 +% 𪧦 U+2A9E6 CJK Unified Ideographs Extension C AJ:14145 +% 𠮷 U+20BB7 CJK Unified Ideographs Extension B AJ:13706 +% 真 U+2F947 CJK Compatibility Ideographs Supplement AJ:13854 +% 冬 U+2F81A CJK Compatibility Ideographs Supplement AJ:13954 +% 𠘨 U+20628 CJK Unified Ideographs Extension B AJ:14105 J4-2332 +% 𫝷 U+2B777 CJK Unified Ideographs Extension D AJ:13782 +% 🄑 U+1F111 Parenthesized Latin Capital Letter B +% 🄓 U+1F113 Parenthesized Latin Capital Letter B +% 🄰 U+1F130 Squared Latin Capital Letter A diff --git a/Build/source/texk/web2c/uptexdir/tests/unibib.tex b/Build/source/texk/web2c/uptexdir/tests/unibib.tex index 918d11f37b2..73e6a744d7a 100644 --- a/Build/source/texk/web2c/uptexdir/tests/unibib.tex +++ b/Build/source/texk/web2c/uptexdir/tests/unibib.tex @@ -82,6 +82,15 @@ publisher = "Snowman commedian Press", year = 2020, } + +%% CJK統合漢字拡張B-Dのテスト +@book{imahashi, + author = "𫝆𫞎 姬 and 𡈽 𪧦 and 𠮷田 真", + yomi = "Hime Imahashi", + title = "CJK統合漢字拡張🄑--🄓のテスト --- その🄰", + publisher = "冬𠘨𫝷", + year = 2024, +} \end{filecontents} \documentclass{ltjsarticle} diff --git a/Build/source/texk/web2c/uptexdir/upbibtex.ch b/Build/source/texk/web2c/uptexdir/upbibtex.ch index 36c44471552..4c21ee3efcd 100644 --- a/Build/source/texk/web2c/uptexdir/upbibtex.ch +++ b/Build/source/texk/web2c/uptexdir/upbibtex.ch @@ -968,7 +968,7 @@ function is_char_kanji_upbibtex(@!c:integer):boolean; label exit; var k:integer; begin - { based on upTeX-1.30 kcatcode status: 16,17,19->true / 15,18->false } + { based on upTeX-1.35 kcatcode status: 16,17,19->true / 15,18->false } is_char_kanji_upbibtex := true; if (is_internalUPTEX) then begin { should be in sync with |kcat_code| of uptex-m.ch } k := kcatcodekey(c); @@ -985,8 +985,8 @@ begin else if k=@"93 then return { Hangul Syllables } else if k=@"94 then return { Hangul Jamo Extended-B } else if k=@"99 then return { CJK Compatibility Ideographs } - else if (k>=@"10D)and(k<=@"110) then return { Kana Extended-B .. Small Kana Extension } - else if (k>=@"13B)and(k<=@"143) then return { CJK Unified Ideographs Extension B .. H } + else if (k>=@"115)and(k<=@"118) then return { Kana Extended-B .. Small Kana Extension } + else if (k>=@"145)and(k<=@"14F) then return { CJK Unified Ideographs Extension B .. J } else if k=@"1FE then return { Fullwidth digit and latin alphabet } else if k=@"1FF then return; { Halfwidth katakana } end diff --git a/Build/source/texk/web2c/uptexdir/uptex-m.ch b/Build/source/texk/web2c/uptexdir/uptex-m.ch index f4b7c7272d9..91a774162c6 100644 --- a/Build/source/texk/web2c/uptexdir/uptex-m.ch +++ b/Build/source/texk/web2c/uptexdir/uptex-m.ch @@ -176,7 +176,6 @@ else if (kcode_pos=1)or((kcode_pos>=@'11)and(kcode_pos<=@'12)) @d other_kchar=18 {kanji codes} @d max_char_code=18 {largest catcode for individual characters} @y -@d latin_ucs=14 {is not cjk characters in ucs code} @d not_cjk=15 {is not cjk characters} @d kanji=16 {kanji} @d kana=17 {hiragana, katakana, alphabet} @@ -239,6 +238,7 @@ for k:=0 to 255 do begin cat_code(k):=other_char; kcat_code(k):=other_kchar; math_code(k):=hi(k); sf_code(k):=1000; auto_xsp_code(k):=0; + inhibit_xsp_type(k):=0; kinsoku_type(k):=0; end; @y eqtb[auto_xspacing_code]:=eqtb[cat_code_base]; @@ -247,6 +247,7 @@ for k:=0 to 255 do begin cat_code(k):=other_char; math_code(k):=hi(k); sf_code(k):=1000; auto_xsp_code(k):=0; + inhibit_xsp_type(k):=0; kinsoku_type(k):=0; end; for k:=0 to 511 do begin kcat_code(k):=other_kchar; @@ -281,12 +282,12 @@ if (isinternalUPTEX) then begin @t\hskip10pt@>kcat_code(@"99):=kanji; { CJK Compatibility Ideographs } @t\hskip10pt@>kcat_code(@"9C):=modifier; { Variation Selectors } { \hskip10pt|kcat_code(@"A2):=other_kchar;| Halfwidth and Fullwidth Forms } - @+@t\1@>for k:=@"10D to @"110 do kcat_code(k):=kana; { Kana Extended-B .. Small Kana Extension } - @+@t\1@>for k:=@"13B to @"143 do kcat_code(k):=kanji; { CJK Unified Ideographs Extension B .. H } - @t\hskip10pt@>kcat_code(@"150):=modifier; { Variation Selectors Supplement } - @+@t\1@>for k:=@"165 to @"166 do kcat_code(k):=kana; { Kana with (Semi-)Voiced Sound Mark } - @t\hskip10pt@>kcat_code(@"16A):=kanji; { Standardized Variation Sequence } - @+@t\1@>for k:=@"16C to @"16D do kcat_code(k):=kanji; { Ideographic Variation Sequence } + @+@t\1@>for k:=@"115 to @"118 do kcat_code(k):=kana; { Kana Extended-B .. Small Kana Extension } + @+@t\1@>for k:=@"145 to @"14F do kcat_code(k):=kanji; { CJK Unified Ideographs Extension B .. J } + @t\hskip10pt@>kcat_code(@"15B):=modifier; { Variation Selectors Supplement } + @+@t\1@>for k:=@"170 to @"171 do kcat_code(k):=kana; { Kana with (Semi-)Voiced Sound Mark } + @t\hskip10pt@>kcat_code(@"175):=kanji; { Standardized Variation Sequence } + @+@t\1@>for k:=@"177 to @"178 do kcat_code(k):=kanji; { Ideographic Variation Sequence } @+@t\1@>for k:=@"1F9 to @"1FC do kcat_code(k):=modifier; { Combining Katakana-Hiragana (Semi-)Voiced Sound Mark .. Emoji Modifier Fitzpatrick } @t\hskip10pt@>kcat_code(@"1FD):=not_cjk; { Latin-1 Letters } @@ -426,7 +427,6 @@ if ((kcp mod @'10)>0)and(nrestmultichr(kcp)>0) then p:=p-(kcp mod @'10); cur_chr:=fromBUFF(ustringcast(buffer), limit+1, loc); cur_cmd:=kcat_code(kcatcodekey(cur_chr)); if (multistrlen(ustringcast(buffer), limit+1, loc)>1) and check_kcat_code(cur_cmd,cur_chr) then begin - if (cur_cmd=latin_ucs) then cur_cmd:=cat_code(cur_chr); if (cur_cmd=not_cjk) then cur_cmd:=other_kchar; for l:=loc to loc-1+multistrlen(ustringcast(buffer), limit+1, loc) do buffer2[l]:=1; @@ -477,7 +477,6 @@ else begin k:=loc; cur_chr:=fromBUFF(ustringcast(buffer), limit+1, k); cat:=kcat_code(kcatcodekey(cur_chr)); if (multistrlen(ustringcast(buffer), limit+1, k)>1) and check_kcat_code(cat,cur_chr) then begin - if (cat=latin_ucs) then cat:=cat_code(cur_chr); if (cat=not_cjk) then cat:=other_kchar; for l:=k to k-1+multistrlen(ustringcast(buffer), limit+1, k) do buffer2[l]:=1; @@ -520,7 +519,6 @@ begin repeat cur_chr:=fromBUFF(ustringcast(buffer), limit+1, k); cat:=kcat_code(kcatcodekey(cur_chr)); if (multistrlen(ustringcast(buffer), limit+1, k)>1) and check_kcat_code(cat,cur_chr) then begin - if (cat=latin_ucs) then cat:=cat_code(cur_chr); if (cat=not_cjk) then cat:=other_kchar; for l:=k to k-1+multistrlen(ustringcast(buffer), limit+1, k) do buffer2[l]:=1; @@ -575,8 +573,6 @@ if cat=other_kchar then k:=k-multilenbuffchar(cur_chr)+1; {now |k| points to fir cur_tok:=(kanji_ivs*max_cjk_val)+cur_chr else cur_tok:=(cur_cmd*max_cjk_val)+cur_chr - else if (cur_cmd=latin_ucs) then - cur_tok:=(cat_code(cur_chr)*max_cjk_val)+cur_chr else cur_tok:=(cur_cmd*max_char_val)+cur_chr @z @@ -610,8 +606,6 @@ if cat=other_kchar then k:=k-multilenbuffchar(cur_chr)+1; {now |k| points to fir cur_tok:=(kanji_ivs*max_cjk_val)+cur_chr else cur_tok:=(cur_cmd*max_cjk_val)+cur_chr - else if (cur_cmd=latin_ucs) then - cur_tok:=(cat_code(cur_chr)*max_cjk_val)+cur_chr else cur_tok:=(cur_cmd*max_char_val)+cur_chr @z @@ -625,8 +619,6 @@ if cat=other_kchar then k:=k-multilenbuffchar(cur_chr)+1; {now |k| points to fir cur_tok:=(kanji_ivs*max_cjk_val)+cur_chr else cur_tok:=(cur_cmd*max_cjk_val)+cur_chr - else if (cur_cmd=latin_ucs) then - cur_tok:=(cat_code(cur_chr)*max_cjk_val)+cur_chr else cur_tok:=(cur_cmd*max_char_val)+cur_chr @z @@ -696,7 +688,6 @@ while k<pool_ptr do begin t:=so(str_pool[k]); if t>=@"180 then { there is no |wchar_token| whose code is 0--127. } begin t:=fromBUFFshort(str_pool, pool_ptr, k); cc:=kcat_code(kcatcodekey(t)); - if (cc=latin_ucs) then cc:=other_char; if (cc=not_cjk) then cc:=other_kchar; if (cc=kanji)and(t>=max_cjk_val) then cc:=kanji_ivs; t:=t+cc*max_cjk_val; @@ -1186,7 +1177,7 @@ kchar_given: begin print_esc("kchar"); print_hex(chr_code); if cur_chr=kcat_code_base then m:=kanji else m:=0 @y @ @<Let |m| be the minimal...@>= -if cur_chr=kcat_code_base then m:=latin_ucs else m:=0 +if cur_chr=kcat_code_base then m:=not_cjk else m:=0 @z @x @@ -1330,8 +1321,6 @@ begin if is_char_node(link(p)) then info(main_p):=KANJI(cur_chr)+cur_cmd*max_cjk_val else if cur_cmd=not_cjk then info(main_p):=KANJI(cur_chr)+other_kchar*max_cjk_val - else if cur_cmd=latin_ucs then - info(main_p):=KANJI(cur_chr)+cat_code(cur_chr)*max_cjk_val else { Does this case occur? } info(main_p):=KANJI(cur_chr)+kcat_code(kcatcodekey(KANJI(cur_chr)))*max_cjk_val; @z @@ -1426,7 +1415,7 @@ end; function check_kcat_code(@!ct:integer;@!cx:integer):integer; begin -if (((ct>=kanji)or((ct=latin_ucs)and(cx<max_ucs_val)))and(enable_cjk_token=0))or(enable_cjk_token=2)then +if ((ct>=kanji)and(enable_cjk_token=0))or(enable_cjk_token=2)then check_kcat_code:=1 else check_kcat_code:=0; end; diff --git a/Build/source/texk/web2c/vftovp.ch b/Build/source/texk/web2c/vftovp.ch index 6b423e33408..052f8be743c 100644 --- a/Build/source/texk/web2c/vftovp.ch +++ b/Build/source/texk/web2c/vftovp.ch @@ -49,7 +49,7 @@ procedure initialize; {this procedure gets things started properly} var @!k:integer; {all-purpose index for initialization} begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(p)|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @!k:integer; {all-purpose index for initialization} begin @@ -546,7 +546,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(p)|parse_arguments|@> = procedure parse_arguments; const n_options = 4; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/vptovf.ch b/Build/source/texk/web2c/vptovf.ch index d9ec8935155..2d15663617e 100644 --- a/Build/source/texk/web2c/vptovf.ch +++ b/Build/source/texk/web2c/vptovf.ch @@ -1,5 +1,11 @@ % vptovf.ch for C compilation with web2c. +@x [0] l.19 +\def\(#1){} % this is used to make section names sort themselves better +@y +\def\({} % this is used to make section names sort themselves better +@z + @x [0] l.22 \def\title{VP\lowercase{to}VF} @y @@ -34,7 +40,7 @@ procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin print_ln(banner);@/ @y -@<Define |parse_arguments|@> +@<Define \(|parse_arguments|@> procedure initialize; {this procedure gets things started properly} var @<Local variables for initialization@>@/ begin kpse_set_program_name (argv[0], my_name); @@ -237,7 +243,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 3; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; diff --git a/Build/source/texk/web2c/weav-twill.ch b/Build/source/texk/web2c/weav-twill.ch index 17c02718758..6678a053932 100644 --- a/Build/source/texk/web2c/weav-twill.ch +++ b/Build/source/texk/web2c/weav-twill.ch @@ -51,9 +51,11 @@ Limbo. \def\title{TWILL for \TeX~Live} @z -@x 48c24 +@x 47c23 + \def\rheader{\mainfont Appendix D\hfil \contentspagenumber} \centerline{\titlefont The {\ttitlefont WEAVE} processor} @y + \def\rheader{\mainfont Appendix D${}'$\hfil \contentspagenumber} \centerline{\titlefont The {\ttitlefont TWILL} processor} @z @@ -249,6 +251,22 @@ Section 71. @y @z +Section 75. + +@x 1323 +@<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>; +@y +@<Move \(|buffer| and |limit| to |change_buffer| and |change_limit|@>; +@z + +Section 78. + +@x 1357 +@ @<Move |buffer| and |limit| to |change_buffer| and |change_limit|@>= +@y +@ @<Move \(|buffer| and |limit| to |change_buffer| and |change_limit|@>= +@z + Section 79. @x 1371,1383d1401 @@ -277,6 +295,12 @@ if not changed_module[module_count] then @y @z +@x 1405 + @<Move |buffer| and |limit|...@>; +@y + @<Move \(|buffer| and |limit|...@>; +@z + Section 82. @x 1450,1454c1463,1469 diff --git a/Build/source/texk/web2c/weave.ch b/Build/source/texk/web2c/weave.ch index 5a932e576ab..9428be4bfd5 100644 --- a/Build/source/texk/web2c/weave.ch +++ b/Build/source/texk/web2c/weave.ch @@ -81,7 +81,7 @@ procedure initialize; const @<Constants in the outer block@>@/ type @<Types in the outer block@>@/ var @<Globals in the outer block@>@/ -@<Define |parse_arguments|@>@/ +@<Define \(|parse_arguments|@>@/ @<Error handling procedures@>@/ procedure initialize; var @<Local variables for initialization@>@/ @@ -159,7 +159,6 @@ for i:=@'200 to @'377 do xchr[i]:=chr(i); @d term_out==stdout @d print(#)==write(term_out,#) {`|print|' means write on the terminal} @z - @x [3.20] l.521 @<Globals...@>= @!term_out:text_file; {the terminal as an output file} @@ -282,7 +281,6 @@ there are programs that need more than 65536 bytes; \TeX\ is one of these (and the pdf\TeX\ variant even requires more than twice that amount when its ``final'' change file is applied). @z - @x [5.37] l.729 is either 0 or 1. (For generality, the first index is actually allowed to run between 0 and |ww-1|, where |ww| is defined to be 2; the program will @@ -290,7 +288,6 @@ run between 0 and |ww-1|, where |ww| is defined to be 2; the program will is either 0, 1 or 2. (For generality, the first index is actually allowed to run between 0 and |ww-1|, where |ww| is defined to be 3; the program will @z - @x [5.37] l.734 @d ww=2 {we multiply the byte capacity by approximately this amount} @y @@ -340,7 +337,6 @@ command line), then we use alternative \TeX\ macros from `\.{\\input pwebmac}'. @.\\input pwebmac@> @.pwebmac@> @z - @x [12.124] l.2204 out_ptr:=1; out_line:=1; out_buf[1]:="c"; write(tex_file,'\input webma'); @y @@ -356,7 +352,6 @@ the break. preceded by another backslash or a \TeX\ comment marker. In the latter case, a |'%'| is output at the break. @z - @x [12.127] l.2248 - deal with malign user input if (d="\")and(out_buf[k-1]<>"\") then {in this case |k>1|} @y @@ -411,7 +406,6 @@ if cat[pp+1]=close then squash(pp,1,stmt,-2)(43) @y if cat[pp+1]=close then reduce(pp,0,stmt,-2)(43) @z - @x [15.167] l.3277 - Apply 'squash(...,2,...)'. begin app(force); app(backup); app2(pp); reduce(pp,2,intro,-3)(44); @y @@ -436,7 +430,6 @@ scrap list. scrap list. This procedure takes advantage of the simplification that occurs when |k=0|. @z - @x [15.172] l.3330 begin cat[j]:=c; trans[j]:=text_ptr; freeze_text; @y @@ -446,7 +439,6 @@ if k>0 then trans[j]:=text_ptr; freeze_text; end; @z - @x [15.172] l.3337 - Fix spacing. @<Change |pp| to $\max(|scrap_base|,|pp+d|)$@>; @y @@ -466,7 +458,6 @@ procedure takes advantage of the simplification that occurs when |k=1|. @ Similarly, the `|squash|' macro invokes a procedure called `|sq|', which combines |app|${}_k$ and |red| for matching numbers~|k|. @z - @x [15.174] l.3349 var i:0..max_scraps; {index into scrap memory} begin if k=1 then @@ -488,6 +479,56 @@ begin red(j,k,c,d); @z +@x [16.185] l.3555 +string,verbatim: @<Append a \(string scrap@>; +identifier: @<Append an identifier scrap@>; +TeX_string: @<Append a \TeX\ string scrap@>; +@y +string,verbatim: @<Append \(a \(string scrap@>; +identifier: @<Append \(an identifier scrap@>; +TeX_string: @<Append \(a \TeX\ string scrap@>; +@z + +@x [16.189] l.3652 +@<Append a \(string scrap@>= +@y +@<Append \(a \(string scrap@>= +@z + +@x [16.190] l.3690 +@ @<Append a \TeX\ string scrap@>= +@y +@ @<Append \(a \TeX\ string scrap@>= +@z + +@x [16.191] l.3697 +@ @<Append an identifier scrap@>= +@y +@ @<Append \(an identifier scrap@>= +@z + +@x [16.193] l.3731 +else_like: begin @<Append |terminator| if not already present@>; +@y +else_like: begin @<Append \(|terminator| if not already present@>; +@z +@x [16.193] l.3734 +end_like: begin @<Append |term...@>; +@y +end_like: begin @<Append \(|term...@>; +@z +@x [16.193] l.3752 +until_like: begin @<Append |term...@>; +@y +until_like: begin @<Append \(|term...@>; +@z + +@x [16.194] l.3762 +@<Append |termin...@>= +@y +@<Append \(|termin...@>= +@z + @x [19.239] l.4537 - omit index and module names if no_xref set @<Phase III: Output the cross-reference index@>= @y @@ -504,7 +545,6 @@ if no_xref then begin end else begin @z - @x [19.239] l.4551 print('Done.'); @y @@ -519,7 +559,6 @@ any error stop will set |debug_cycle| to zero. @d term_in==stdin @z - @x [20.258] l.4790 @!term_in:text_file; {the user's terminal as an input file} @y @@ -536,7 +575,6 @@ print_ln(banner); {print a ``banner line''} print (banner); {print a ``banner line''} print_ln (version_string); @z - @x [21.261] l.4856 end_of_WEAVE: stat @<Print statistics about memory usage@>;@+tats@;@/ @@ -562,7 +600,7 @@ Parse a Unix-style command line. @d argument_is (#) == (strcmp (long_options[option_index].name, #) = 0) -@<Define |parse_arguments|@> = +@<Define \(|parse_arguments|@> = procedure parse_arguments; const n_options = 4; {Pascal won't count array lengths for us.} var @!long_options: array[0..n_options] of getopt_struct; |