From 307dd6b08adb9d64007e887bd99927bf55567f13 Mon Sep 17 00:00:00 2001 From: Karl Berry Date: Tue, 21 Sep 2021 16:46:46 +0000 Subject: hitexdir: initial import, omitted from build by default git-svn-id: svn://tug.org/texlive/trunk@60569 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/configure | 17 + Build/source/texk/web2c/ChangeLog | 6 + Build/source/texk/web2c/Makefile.am | 6 +- Build/source/texk/web2c/Makefile.in | 809 +- Build/source/texk/web2c/ac/web2c.ac | 3 +- Build/source/texk/web2c/c-auto.in | 2 +- Build/source/texk/web2c/configure | 30 + Build/source/texk/web2c/configure.ac | 2 +- Build/source/texk/web2c/hitexdir/am/hitex.am | 100 + Build/source/texk/web2c/hitexdir/format.w | 10509 +++++++++ Build/source/texk/web2c/hitexdir/hitex.w | 4753 ++++ Build/source/texk/web2c/hitexdir/htex.w | 29180 +++++++++++++++++++++++++ Build/source/texk/web2c/hitexdir/lex.sed | 5 + Build/source/texk/web2c/hitexdir/yacc.sed | 3 + 14 files changed, 45184 insertions(+), 241 deletions(-) create mode 100644 Build/source/texk/web2c/hitexdir/am/hitex.am create mode 100644 Build/source/texk/web2c/hitexdir/format.w create mode 100644 Build/source/texk/web2c/hitexdir/hitex.w create mode 100644 Build/source/texk/web2c/hitexdir/htex.w create mode 100644 Build/source/texk/web2c/hitexdir/lex.sed create mode 100644 Build/source/texk/web2c/hitexdir/yacc.sed (limited to 'Build/source/texk') diff --git a/Build/source/texk/configure b/Build/source/texk/configure index d0a121e16f6..81475012866 100755 --- a/Build/source/texk/configure +++ b/Build/source/texk/configure @@ -765,6 +765,7 @@ enable_uptex_synctex enable_euptex enable_euptex_synctex enable_aleph +enable_hitex enable_pdftex enable_pdftex_synctex enable_luatex @@ -1560,6 +1561,7 @@ Optional Features: --disable-euptex do not compile and install e-upTeX --disable-euptex-synctex build e-upTeX without SyncTeX support --disable-aleph do not compile and install Aleph + --enable-hitex compile and install HiTeX --disable-pdftex do not compile and install pdfTeX --disable-pdftex-synctex build pdfTeX without SyncTeX support --disable-luatex do not compile and install LuaTeX @@ -4560,6 +4562,21 @@ case $enable_aleph in #( *) : enable_aleph=yes ;; esac +# Check whether --enable-hitex was given. +if test ${enable_hitex+y} +then : + enableval=$enable_hitex; +fi +case $enable_hitex in #( + yes | no) : + ;; #( + *) : + enable_hitex=no ;; +esac + +test "x$enable_web2c:$enable_hitex" = xyes:yes && { + need_zlib=yes +} # Check whether --enable-pdftex was given. if test ${enable_pdftex+y} then : diff --git a/Build/source/texk/web2c/ChangeLog b/Build/source/texk/web2c/ChangeLog index 480f59239f6..ef1f540253c 100644 --- a/Build/source/texk/web2c/ChangeLog +++ b/Build/source/texk/web2c/ChangeLog @@ -1,3 +1,9 @@ +2021-09-21 Karl Berry + + * ac/web2c.ac (kpse_tex_progs): include hitex, disabled by default. + * Makefile.am: include hitexdir/am/hitex.am. + * hitexdir: new directory. + 2021-09-16 Andreas Scherer * Makefile.in, diff --git a/Build/source/texk/web2c/Makefile.am b/Build/source/texk/web2c/Makefile.am index 0ad8e4db3ab..1d8f29f4d63 100644 --- a/Build/source/texk/web2c/Makefile.am +++ b/Build/source/texk/web2c/Makefile.am @@ -274,6 +274,9 @@ include $(srcdir)/uptexdir/am/uptex.am ## e-upTeX include $(srcdir)/euptexdir/am/euptex.am +## hiTeX +include $(srcdir)/hitexdir/am/hitex.am + ## pdfTeX include $(srcdir)/pdftexdir/am/libpdftex.am include $(srcdir)/pdftexdir/am/pdftex.am @@ -289,12 +292,9 @@ include $(srcdir)/luatexdir/am/libluatex.am include $(srcdir)/luatexdir/am/luaffi.am include $(srcdir)/luatexdir/am/luatex.am - ## luahbTeX include $(srcdir)/luatexdir/am/luaharfbuzz.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 eab99a2e180..7273a62c1ed 100644 --- a/Build/source/texk/web2c/Makefile.in +++ b/Build/source/texk/web2c/Makefile.in @@ -112,7 +112,8 @@ EXTRA_PROGRAMS = $(am__EXEEXT_1) tex$(EXEEXT) mf$(EXEEXT) \ mfluajit$(EXEEXT) mfluajit-nowin$(EXEEXT) mpost$(EXEEXT) \ pmpost$(EXEEXT) upmpost$(EXEEXT) etex$(EXEEXT) ptex$(EXEEXT) \ $(am__EXEEXT_2) eptex$(EXEEXT) uptex$(EXEEXT) $(am__EXEEXT_3) \ - euptex$(EXEEXT) pdftex$(EXEEXT) ttf2afm$(EXEEXT) \ + euptex$(EXEEXT) hitex$(EXEEXT) hishrink$(EXEEXT) \ + histretch$(EXEEXT) pdftex$(EXEEXT) ttf2afm$(EXEEXT) \ pdftosrc$(EXEEXT) luatex$(EXEEXT) luajittex$(EXEEXT) \ luahbtex$(EXEEXT) luajithbtex$(EXEEXT) xetex$(EXEEXT) \ $(am__EXEEXT_4) aleph$(EXEEXT) synctex$(EXEEXT) \ @@ -128,22 +129,23 @@ bin_PROGRAMS = tangle$(EXEEXT) ctangle$(EXEEXT) $(am__EXEEXT_5) \ $(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_31) $(am__EXEEXT_32) + $(am__EXEEXT_30) $(am__EXEEXT_31) $(am__EXEEXT_32) \ + $(am__EXEEXT_33) noinst_PROGRAMS = tangleboot$(EXEEXT) ctangleboot$(EXEEXT) \ - $(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 \ + $(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) \ + $(am__EXEEXT_43) +TESTS = tangle.test $(am__EXEEXT_46) ctiedir/ctie.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_63) $(am__append_66) \ $(am__append_68) $(am__append_73) $(am__append_76) \ - $(am__append_78) $(am__append_83) $(am__append_104) \ - $(am__append_105) $(am__append_106) $(am__append_107) \ - $(am__append_115) $(am__append_117) $(am__append_119) \ - $(am__append_153) libmd5/md5.test + $(am__append_78) $(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_154) libmd5/md5.test @WEB_TRUE@am__append_1 = $(web_programs) @WEB_TRUE@am__append_2 = $(web_tests) @TEX_TRUE@am__append_3 = tex @@ -228,150 +230,151 @@ TESTS = tangle.test $(am__EXEEXT_45) ctiedir/ctie.test \ @EUPTEX_TRUE@am__append_78 = $(euptex_tests) @EUPTEX_TRUE@am__append_79 = euptrip.diffs @EUPTEX_TRUE@am__append_80 = euptrip-clean -@MINGW32_TRUE@am__append_81 = \ +@HITEX_TRUE@am__append_81 = hitex hishrink histretch +@MINGW32_TRUE@am__append_82 = \ @MINGW32_TRUE@ pdftexdir/regex/regex.c \ @MINGW32_TRUE@ pdftexdir/regex/regex.h -@PDFTEX_TRUE@am__append_82 = pdftex ttf2afm pdftosrc -@PDFTEX_TRUE@am__append_83 = $(pdftex_tests) $(ttf2afm_tests) \ +@PDFTEX_TRUE@am__append_83 = pdftex ttf2afm pdftosrc +@PDFTEX_TRUE@am__append_84 = $(pdftex_tests) $(ttf2afm_tests) \ @PDFTEX_TRUE@ $(pdftosrc_tests) -@LUATEX_TRUE@am__append_84 = luatex -@LUATEX_TRUE@@WIN32_TRUE@am__append_85 = call_luatex -@LUATEX_TRUE@@WIN32_TRUE@am__append_86 = install-luatex-links -@LUATEX_TRUE@@WIN32_TRUE@am__append_87 = uninstall-luatex-links -@LUATEX_TRUE@@WIN32_FALSE@am__append_88 = luatex$(EXEEXT):texlua luatex$(EXEEXT):texluac -@LUAHBTEX_TRUE@am__append_89 = luahbtex -@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_90 = call_luahbtex -@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_91 = install-luahbtex-links -@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_92 = uninstall-luahbtex-links +@LUATEX_TRUE@am__append_85 = luatex +@LUATEX_TRUE@@WIN32_TRUE@am__append_86 = call_luatex +@LUATEX_TRUE@@WIN32_TRUE@am__append_87 = install-luatex-links +@LUATEX_TRUE@@WIN32_TRUE@am__append_88 = uninstall-luatex-links +@LUATEX_TRUE@@WIN32_FALSE@am__append_89 = luatex$(EXEEXT):texlua luatex$(EXEEXT):texluac +@LUAHBTEX_TRUE@am__append_90 = luahbtex +@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_91 = call_luahbtex +@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_92 = install-luahbtex-links +@LUAHBTEX_TRUE@@WIN32_TRUE@am__append_93 = uninstall-luahbtex-links # keep texlua[c] as links to luatex unless luatex is not installed. -@LUAHBTEX_TRUE@@LUATEX_FALSE@@WIN32_FALSE@am__append_93 = luahbtex$(EXEEXT):texlua luahbtex$(EXEEXT):texluac -@LUAJITTEX_TRUE@am__append_94 = luajittex -@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_95 = call_luajittex -@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_96 = install-luajittex-links -@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_97 = uninstall-luajittex-links -@LUAJITTEX_TRUE@@WIN32_FALSE@am__append_98 = luajittex$(EXEEXT):texluajit luajittex$(EXEEXT):texluajitc -@LUAJITHBTEX_TRUE@am__append_99 = luajithbtex -@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_100 = call_luajithbtex -@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_101 = install-luajithbtex-links -@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_102 = uninstall-luajithbtex-links +@LUAHBTEX_TRUE@@LUATEX_FALSE@@WIN32_FALSE@am__append_94 = luahbtex$(EXEEXT):texlua luahbtex$(EXEEXT):texluac +@LUAJITTEX_TRUE@am__append_95 = luajittex +@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_96 = call_luajittex +@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_97 = install-luajittex-links +@LUAJITTEX_TRUE@@WIN32_TRUE@am__append_98 = uninstall-luajittex-links +@LUAJITTEX_TRUE@@WIN32_FALSE@am__append_99 = luajittex$(EXEEXT):texluajit luajittex$(EXEEXT):texluajitc +@LUAJITHBTEX_TRUE@am__append_100 = luajithbtex +@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_101 = call_luajithbtex +@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_102 = install-luajithbtex-links +@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__append_103 = uninstall-luajithbtex-links # keep texluajit[c] as links to luajittex unless luajittex is not installed. -@LUAJITHBTEX_TRUE@@LUAJITTEX_FALSE@@WIN32_FALSE@am__append_103 = luajithbtex$(EXEEXT):texluajit luajithbtex$(EXEEXT):texluajitc -@LUATEX_TRUE@am__append_104 = $(luatex_tests) -@LUAHBTEX_TRUE@am__append_105 = $(luahbtex_tests) -@LUAJITTEX_TRUE@am__append_106 = $(luajittex_tests) -@LUAJITHBTEX_TRUE@am__append_107 = $(luajithbtex_tests) -@XETEX_TRUE@am__append_108 = xetex -@XETEX_MACOSX_TRUE@am__append_109 = -DXETEX_MAC -@XETEX_MACOSX_TRUE@am__append_110 = -std=c++11 -@XETEX_MACOSX_FALSE@am__append_111 = $(FONTCONFIG_INCLUDES) -@XETEX_MACOSX_FALSE@am__append_112 = $(FONTCONFIG_LIBS) -@XETEX_MACOSX_TRUE@am__append_113 = \ +@LUAJITHBTEX_TRUE@@LUAJITTEX_FALSE@@WIN32_FALSE@am__append_104 = luajithbtex$(EXEEXT):texluajit luajithbtex$(EXEEXT):texluajitc +@LUATEX_TRUE@am__append_105 = $(luatex_tests) +@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 = \ @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_114 = \ +@XETEX_MACOSX_FALSE@am__append_115 = \ @XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.cpp \ @XETEX_MACOSX_FALSE@ xetexdir/XeTeXFontMgr_FC.h -@XETEX_TRUE@am__append_115 = $(xetex_tests) -@OTANGLE_TRUE@am__append_116 = $(omegaware_programs) -@OTANGLE_TRUE@am__append_117 = $(OTANGLE_tests) $(OMFONTS_tests) -@ALEPH_TRUE@am__append_118 = aleph -@ALEPH_TRUE@am__append_119 = $(aleph_tests) -@SYNCTEX_TRUE@am__append_120 = synctex -@SYNCTEX_TRUE@am__append_121 = $(LTLIBSYNCTEX) -@SYNCTEX_TRUE@am__append_122 = $(LIBSYNCTEX) -@MINGW32_TRUE@am__append_123 = -lshlwapi +@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 = -lshlwapi -@TEX_SYNCTEX_TRUE@am__append_125 = -I$(srcdir)/synctexdir \ +@MINGW32_TRUE@am__append_125 = -lshlwapi +@TEX_SYNCTEX_TRUE@am__append_126 = -I$(srcdir)/synctexdir \ @TEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @TEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-tex.h\" -@TEX_SYNCTEX_TRUE@am__append_126 = $(ZLIB_LIBS) -@TEX_SYNCTEX_TRUE@am__append_127 = $(ZLIB_DEPEND) -@TEX_SYNCTEX_TRUE@am__append_128 = \ +@TEX_SYNCTEX_TRUE@am__append_127 = $(ZLIB_LIBS) +@TEX_SYNCTEX_TRUE@am__append_128 = $(ZLIB_DEPEND) +@TEX_SYNCTEX_TRUE@am__append_129 = \ @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_129 = -I$(srcdir)/synctexdir \ +@ETEX_SYNCTEX_TRUE@am__append_130 = -I$(srcdir)/synctexdir \ @ETEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @ETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-etex.h\" -@ETEX_SYNCTEX_TRUE@am__append_130 = $(ZLIB_LIBS) -@ETEX_SYNCTEX_TRUE@am__append_131 = $(ZLIB_DEPEND) -@ETEX_SYNCTEX_TRUE@am__append_132 = \ +@ETEX_SYNCTEX_TRUE@am__append_131 = $(ZLIB_LIBS) +@ETEX_SYNCTEX_TRUE@am__append_132 = $(ZLIB_DEPEND) +@ETEX_SYNCTEX_TRUE@am__append_133 = \ @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 -@PTEX_SYNCTEX_TRUE@am__append_133 = -I$(srcdir)/synctexdir \ +@PTEX_SYNCTEX_TRUE@am__append_134 = -I$(srcdir)/synctexdir \ @PTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @PTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-ptex.h\" -@PTEX_SYNCTEX_TRUE@am__append_134 = $(ZLIB_LIBS) -@PTEX_SYNCTEX_TRUE@am__append_135 = $(ZLIB_DEPEND) -@PTEX_SYNCTEX_TRUE@am__append_136 = \ +@PTEX_SYNCTEX_TRUE@am__append_135 = $(ZLIB_LIBS) +@PTEX_SYNCTEX_TRUE@am__append_136 = $(ZLIB_DEPEND) +@PTEX_SYNCTEX_TRUE@am__append_137 = \ @PTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @PTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @PTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @PTEX_SYNCTEX_TRUE@ synctexdir/synctex-ptex.h -@UPTEX_SYNCTEX_TRUE@am__append_137 = -I$(srcdir)/synctexdir \ +@UPTEX_SYNCTEX_TRUE@am__append_138 = -I$(srcdir)/synctexdir \ @UPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @UPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-uptex.h\" -@UPTEX_SYNCTEX_TRUE@am__append_138 = $(ZLIB_LIBS) -@UPTEX_SYNCTEX_TRUE@am__append_139 = $(ZLIB_DEPEND) -@UPTEX_SYNCTEX_TRUE@am__append_140 = \ +@UPTEX_SYNCTEX_TRUE@am__append_139 = $(ZLIB_LIBS) +@UPTEX_SYNCTEX_TRUE@am__append_140 = $(ZLIB_DEPEND) +@UPTEX_SYNCTEX_TRUE@am__append_141 = \ @UPTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @UPTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @UPTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @UPTEX_SYNCTEX_TRUE@ synctexdir/synctex-uptex.h -@EPTEX_SYNCTEX_TRUE@am__append_141 = -I$(srcdir)/synctexdir \ +@EPTEX_SYNCTEX_TRUE@am__append_142 = -I$(srcdir)/synctexdir \ @EPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @EPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-eptex.h\" -@EPTEX_SYNCTEX_TRUE@am__append_142 = $(ZLIB_LIBS) -@EPTEX_SYNCTEX_TRUE@am__append_143 = $(ZLIB_DEPEND) -@EPTEX_SYNCTEX_TRUE@am__append_144 = \ +@EPTEX_SYNCTEX_TRUE@am__append_143 = $(ZLIB_LIBS) +@EPTEX_SYNCTEX_TRUE@am__append_144 = $(ZLIB_DEPEND) +@EPTEX_SYNCTEX_TRUE@am__append_145 = \ @EPTEX_SYNCTEX_TRUE@ synctexdir/synctex.c \ @EPTEX_SYNCTEX_TRUE@ synctexdir/synctex.h \ @EPTEX_SYNCTEX_TRUE@ synctexdir/synctex-common.h \ @EPTEX_SYNCTEX_TRUE@ synctexdir/synctex-eptex.h -@EUPTEX_SYNCTEX_TRUE@am__append_145 = -I$(srcdir)/synctexdir \ +@EUPTEX_SYNCTEX_TRUE@am__append_146 = -I$(srcdir)/synctexdir \ @EUPTEX_SYNCTEX_TRUE@ $(ZLIB_INCLUDES) -D__SyncTeX__ \ @EUPTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-euptex.h\" -@EUPTEX_SYNCTEX_TRUE@am__append_146 = $(ZLIB_LIBS) -@EUPTEX_SYNCTEX_TRUE@am__append_147 = $(ZLIB_DEPEND) -@EUPTEX_SYNCTEX_TRUE@am__append_148 = \ +@EUPTEX_SYNCTEX_TRUE@am__append_147 = $(ZLIB_LIBS) +@EUPTEX_SYNCTEX_TRUE@am__append_148 = $(ZLIB_DEPEND) +@EUPTEX_SYNCTEX_TRUE@am__append_149 = \ @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_149 = -I$(srcdir)/synctexdir \ +@PDFTEX_SYNCTEX_TRUE@am__append_150 = -I$(srcdir)/synctexdir \ @PDFTEX_SYNCTEX_TRUE@ -D__SyncTeX__ \ @PDFTEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-pdftex.h\" -@PDFTEX_SYNCTEX_TRUE@am__append_150 = \ +@PDFTEX_SYNCTEX_TRUE@am__append_151 = \ @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_151 = -I$(srcdir)/synctexdir \ +@XETEX_SYNCTEX_TRUE@am__append_152 = -I$(srcdir)/synctexdir \ @XETEX_SYNCTEX_TRUE@ -D__SyncTeX__ \ @XETEX_SYNCTEX_TRUE@ -DSYNCTEX_ENGINE_H=\"synctex-xetex.h\" -@XETEX_SYNCTEX_TRUE@am__append_152 = \ +@XETEX_SYNCTEX_TRUE@am__append_153 = \ @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_153 = $(synctex_tests) +@SYNCTEX_TRUE@am__append_154 = $(synctex_tests) subdir = . ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/web2c-disable.m4 \ @@ -461,30 +464,32 @@ am__EXEEXT_4 = odvicopy$(EXEEXT) odvitype$(EXEEXT) otangle$(EXEEXT) \ @UPTEX_TRUE@am__EXEEXT_21 = uptex$(EXEEXT) @UPWEB_TRUE@am__EXEEXT_22 = $(am__EXEEXT_3) @EUPTEX_TRUE@am__EXEEXT_23 = euptex$(EXEEXT) -@PDFTEX_TRUE@am__EXEEXT_24 = pdftex$(EXEEXT) ttf2afm$(EXEEXT) \ +@HITEX_TRUE@am__EXEEXT_24 = hitex$(EXEEXT) hishrink$(EXEEXT) \ +@HITEX_TRUE@ histretch$(EXEEXT) +@PDFTEX_TRUE@am__EXEEXT_25 = pdftex$(EXEEXT) ttf2afm$(EXEEXT) \ @PDFTEX_TRUE@ pdftosrc$(EXEEXT) -@LUATEX_TRUE@am__EXEEXT_25 = luatex$(EXEEXT) -@LUAHBTEX_TRUE@am__EXEEXT_26 = luahbtex$(EXEEXT) -@LUAJITTEX_TRUE@am__EXEEXT_27 = luajittex$(EXEEXT) -@LUAJITHBTEX_TRUE@am__EXEEXT_28 = luajithbtex$(EXEEXT) -@XETEX_TRUE@am__EXEEXT_29 = xetex$(EXEEXT) -@OTANGLE_TRUE@am__EXEEXT_30 = $(am__EXEEXT_4) -@ALEPH_TRUE@am__EXEEXT_31 = aleph$(EXEEXT) -@SYNCTEX_TRUE@am__EXEEXT_32 = synctex$(EXEEXT) +@LUATEX_TRUE@am__EXEEXT_26 = luatex$(EXEEXT) +@LUAHBTEX_TRUE@am__EXEEXT_27 = luahbtex$(EXEEXT) +@LUAJITTEX_TRUE@am__EXEEXT_28 = luajittex$(EXEEXT) +@LUAJITHBTEX_TRUE@am__EXEEXT_29 = luajithbtex$(EXEEXT) +@XETEX_TRUE@am__EXEEXT_30 = xetex$(EXEEXT) +@OTANGLE_TRUE@am__EXEEXT_31 = $(am__EXEEXT_4) +@ALEPH_TRUE@am__EXEEXT_32 = aleph$(EXEEXT) +@SYNCTEX_TRUE@am__EXEEXT_33 = synctex$(EXEEXT) am__installdirs = "$(DESTDIR)$(bindir)" "$(DESTDIR)$(libdir)" \ "$(DESTDIR)$(libdir)" "$(DESTDIR)$(man1dir)" \ "$(DESTDIR)$(man5dir)" "$(DESTDIR)$(pkgconfigdir)" \ "$(DESTDIR)$(syncincludedir)" -@MF_TRUE@@WIN32_TRUE@am__EXEEXT_33 = call_mf$(EXEEXT) -@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mflua$(EXEEXT) -@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_mfluajit$(EXEEXT) -@MP_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_mpost$(EXEEXT) -@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_37 = call_pmpost$(EXEEXT) -@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_38 = call_upmpost$(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 = \ +@MF_TRUE@@WIN32_TRUE@am__EXEEXT_34 = call_mf$(EXEEXT) +@MFLUA_TRUE@@WIN32_TRUE@am__EXEEXT_35 = call_mflua$(EXEEXT) +@MFLUAJIT_TRUE@@WIN32_TRUE@am__EXEEXT_36 = call_mfluajit$(EXEEXT) +@MP_TRUE@@WIN32_TRUE@am__EXEEXT_37 = call_mpost$(EXEEXT) +@PMP_TRUE@@WIN32_TRUE@am__EXEEXT_38 = call_pmpost$(EXEEXT) +@UPMP_TRUE@@WIN32_TRUE@am__EXEEXT_39 = call_upmpost$(EXEEXT) +@LUATEX_TRUE@@WIN32_TRUE@am__EXEEXT_40 = call_luatex$(EXEEXT) +@LUAHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_41 = call_luahbtex$(EXEEXT) +@LUAJITTEX_TRUE@@WIN32_TRUE@am__EXEEXT_42 = call_luajittex$(EXEEXT) +@LUAJITHBTEX_TRUE@@WIN32_TRUE@am__EXEEXT_43 = \ @LUAJITHBTEX_TRUE@@WIN32_TRUE@ call_luajithbtex$(EXEEXT) PROGRAMS = $(bin_PROGRAMS) $(noinst_PROGRAMS) am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; @@ -1382,12 +1387,27 @@ nodist_gftype_OBJECTS = gftype.$(OBJEXT) gftype_OBJECTS = $(nodist_gftype_OBJECTS) gftype_LDADD = $(LDADD) gftype_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1) +nodist_hishrink_OBJECTS = hishrink-hformat.$(OBJEXT) \ + hishrink-hishrink.$(OBJEXT) hishrink-shrink-lexer.$(OBJEXT) \ + hishrink-shrink-parser.$(OBJEXT) +hishrink_OBJECTS = $(nodist_hishrink_OBJECTS) +hishrink_DEPENDENCIES = $(am__DEPENDENCIES_1) +nodist_histretch_OBJECTS = histretch-hformat.$(OBJEXT) \ + histretch-histretch.$(OBJEXT) +histretch_OBJECTS = $(nodist_histretch_OBJECTS) +histretch_DEPENDENCIES = $(am__DEPENDENCIES_1) +am__objects_37 = hitex-htex.$(OBJEXT) +am__objects_38 = hitex-hitex.$(OBJEXT) +nodist_hitex_OBJECTS = hitex-hformat.$(OBJEXT) hitex-hput.$(OBJEXT) \ + $(am__objects_37) $(am__objects_38) +hitex_OBJECTS = $(nodist_hitex_OBJECTS) +hitex_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) nodist_initex_OBJECTS = initex-callexe.$(OBJEXT) initex_OBJECTS = $(nodist_initex_OBJECTS) initex_DEPENDENCIES = -am__objects_37 = luatexdir/luahbtex-luatex.$(OBJEXT) \ +am__objects_39 = luatexdir/luahbtex-luatex.$(OBJEXT) \ mplibdir/luahbtex-lmplib.$(OBJEXT) -nodist_luahbtex_OBJECTS = $(am__objects_37) +nodist_luahbtex_OBJECTS = $(am__objects_39) luahbtex_OBJECTS = $(nodist_luahbtex_OBJECTS) am__DEPENDENCIES_6 = $(am__DEPENDENCIES_1) am__DEPENDENCIES_7 = libmplibcore.a $(am__DEPENDENCIES_1) \ @@ -1398,16 +1418,16 @@ am__DEPENDENCIES_7 = libmplibcore.a $(am__DEPENDENCIES_1) \ luahbtex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(luahbtex_CXXFLAGS) \ $(CXXFLAGS) $(luahbtex_LDFLAGS) $(LDFLAGS) -o $@ -am__objects_38 = luatexdir/luajithbtex-luatex.$(OBJEXT) \ +am__objects_40 = luatexdir/luajithbtex-luatex.$(OBJEXT) \ mplibdir/luajithbtex-lmplib.$(OBJEXT) -nodist_luajithbtex_OBJECTS = $(am__objects_38) +nodist_luajithbtex_OBJECTS = $(am__objects_40) luajithbtex_OBJECTS = $(nodist_luajithbtex_OBJECTS) luajithbtex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CXX $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CXXLD) $(luajithbtex_CXXFLAGS) \ $(CXXFLAGS) $(luajithbtex_LDFLAGS) $(LDFLAGS) -o $@ -am__objects_39 = luatexdir/luajittex-luatex.$(OBJEXT) \ +am__objects_41 = luatexdir/luajittex-luatex.$(OBJEXT) \ mplibdir/luajittex-lmplib.$(OBJEXT) -nodist_luajittex_OBJECTS = $(am__objects_39) +nodist_luajittex_OBJECTS = $(am__objects_41) luajittex_OBJECTS = $(nodist_luajittex_OBJECTS) am__DEPENDENCIES_8 = libmplibcore.a $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -1416,9 +1436,9 @@ am__DEPENDENCIES_8 = libmplibcore.a $(am__DEPENDENCIES_1) \ luajittex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(luajittex_LDFLAGS) $(LDFLAGS) -o $@ -am__objects_40 = luatexdir/luatex-luatex.$(OBJEXT) \ +am__objects_42 = luatexdir/luatex-luatex.$(OBJEXT) \ mplibdir/luatex-lmplib.$(OBJEXT) -nodist_luatex_OBJECTS = $(am__objects_40) +nodist_luatex_OBJECTS = $(am__objects_42) luatex_OBJECTS = $(nodist_luatex_OBJECTS) luatex_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ @@ -1444,8 +1464,8 @@ mflua_DEPENDENCIES = libmflua.a libmfluaotfcc.a $(am__DEPENDENCIES_2) \ mflua_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(mflua_LDFLAGS) $(LDFLAGS) -o $@ -am__objects_41 = mfluadir/mflua_nowin-mfluaextra.$(OBJEXT) -nodist_mflua_nowin_OBJECTS = $(am__objects_41) +am__objects_43 = mfluadir/mflua_nowin-mfluaextra.$(OBJEXT) +nodist_mflua_nowin_OBJECTS = $(am__objects_43) mflua_nowin_OBJECTS = $(nodist_mflua_nowin_OBJECTS) mflua_nowin_DEPENDENCIES = libmflua.a libmfluaotfcc.a \ $(am__DEPENDENCIES_2) $(windowlib) $(am__DEPENDENCIES_6) @@ -1460,8 +1480,8 @@ mfluajit_DEPENDENCIES = libmfluajit.a libmfluaotfcc.a \ mfluajit_LINK = $(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) \ $(LIBTOOLFLAGS) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ $(mfluajit_LDFLAGS) $(LDFLAGS) -o $@ -am__objects_42 = mfluajitdir/mfluajit_nowin-mfluajitextra.$(OBJEXT) -nodist_mfluajit_nowin_OBJECTS = $(am__objects_42) +am__objects_44 = mfluajitdir/mfluajit_nowin-mfluajitextra.$(OBJEXT) +nodist_mfluajit_nowin_OBJECTS = $(am__objects_44) mfluajit_nowin_OBJECTS = $(nodist_mfluajit_nowin_OBJECTS) mfluajit_nowin_DEPENDENCIES = libmfluajit.a libmfluaotfcc.a \ $(am__DEPENDENCIES_2) $(windowlib) $(am__DEPENDENCIES_1) @@ -1473,8 +1493,8 @@ nodist_mft_OBJECTS = mft.$(OBJEXT) mft_OBJECTS = $(nodist_mft_OBJECTS) mft_LDADD = $(LDADD) mft_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1) -am__objects_43 = mpost-mpxout.$(OBJEXT) -nodist_mpost_OBJECTS = mpost-mpost.$(OBJEXT) $(am__objects_43) +am__objects_45 = mpost-mpxout.$(OBJEXT) +nodist_mpost_OBJECTS = mpost-mpost.$(OBJEXT) $(am__objects_45) mpost_OBJECTS = $(nodist_mpost_OBJECTS) mpost_DEPENDENCIES = libmplibcore.a libmplibextramath.a \ libmplibbackends.a $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -1507,12 +1527,12 @@ am__dist_pdftex_SOURCES_DIST = pdftexdir/pdftexextra.c \ pdftexdir/etex_version.h synctexdir/synctex.c \ synctexdir/synctex.h synctexdir/synctex-common.h \ synctexdir/synctex-pdftex.h -@PDFTEX_SYNCTEX_TRUE@am__objects_44 = \ +@PDFTEX_SYNCTEX_TRUE@am__objects_46 = \ @PDFTEX_SYNCTEX_TRUE@ synctexdir/pdftex-synctex.$(OBJEXT) dist_pdftex_OBJECTS = pdftexdir/pdftex-pdftexextra.$(OBJEXT) \ - $(am__objects_44) -am__objects_45 = pdftex-pdftexini.$(OBJEXT) pdftex-pdftex0.$(OBJEXT) -nodist_pdftex_OBJECTS = $(am__objects_45) pdftex-pdftex-pool.$(OBJEXT) + $(am__objects_46) +am__objects_47 = pdftex-pdftexini.$(OBJEXT) pdftex-pdftex0.$(OBJEXT) +nodist_pdftex_OBJECTS = $(am__objects_47) pdftex-pdftex-pool.$(OBJEXT) pdftex_OBJECTS = $(dist_pdftex_OBJECTS) $(nodist_pdftex_OBJECTS) am__DEPENDENCIES_11 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) libmd5.a @@ -1539,20 +1559,20 @@ nodist_pltotf_OBJECTS = pltotf.$(OBJEXT) pltotf_OBJECTS = $(nodist_pltotf_OBJECTS) pltotf_LDADD = $(LDADD) pltotf_DEPENDENCIES = $(proglib) $(am__DEPENDENCIES_1) -am__objects_46 = pmpost-pmp.$(OBJEXT) -am__objects_47 = pmpost-pmpmath.$(OBJEXT) -am__objects_48 = pmpost-pmpmathbinary.$(OBJEXT) -am__objects_49 = pmpost-pmpmathdecimal.$(OBJEXT) -am__objects_50 = pmpost-pmpmathdouble.$(OBJEXT) -am__objects_51 = pmpost-pmpstrings.$(OBJEXT) -am__objects_52 = pmpost-pmpxout.$(OBJEXT) -am__objects_53 = pmpost-ppngout.$(OBJEXT) -am__objects_54 = pmpost-ppsout.$(OBJEXT) -am__objects_55 = pmpost-psvgout.$(OBJEXT) -nodist_pmpost_OBJECTS = $(am__objects_46) $(am__objects_47) \ - $(am__objects_48) $(am__objects_49) $(am__objects_50) \ - pmpost-pmpost.$(OBJEXT) $(am__objects_51) $(am__objects_52) \ - $(am__objects_53) $(am__objects_54) $(am__objects_55) \ +am__objects_48 = pmpost-pmp.$(OBJEXT) +am__objects_49 = pmpost-pmpmath.$(OBJEXT) +am__objects_50 = pmpost-pmpmathbinary.$(OBJEXT) +am__objects_51 = pmpost-pmpmathdecimal.$(OBJEXT) +am__objects_52 = pmpost-pmpmathdouble.$(OBJEXT) +am__objects_53 = pmpost-pmpstrings.$(OBJEXT) +am__objects_54 = pmpost-pmpxout.$(OBJEXT) +am__objects_55 = pmpost-ppngout.$(OBJEXT) +am__objects_56 = pmpost-ppsout.$(OBJEXT) +am__objects_57 = pmpost-psvgout.$(OBJEXT) +nodist_pmpost_OBJECTS = $(am__objects_48) $(am__objects_49) \ + $(am__objects_50) $(am__objects_51) $(am__objects_52) \ + pmpost-pmpost.$(OBJEXT) $(am__objects_53) $(am__objects_54) \ + $(am__objects_55) $(am__objects_56) $(am__objects_57) \ pmpost-ptfmin.$(OBJEXT) pmpost_OBJECTS = $(nodist_pmpost_OBJECTS) pmpost_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -1570,10 +1590,10 @@ am__dist_ptex_SOURCES_DIST = ptexdir/ptexextra.c ptexdir/ptexextra.h \ ptexdir/ptex_version.h synctexdir/synctex.c \ synctexdir/synctex.h synctexdir/synctex-common.h \ synctexdir/synctex-ptex.h -@PTEX_SYNCTEX_TRUE@am__objects_56 = synctexdir/ptex-synctex.$(OBJEXT) -dist_ptex_OBJECTS = ptexdir/ptex-ptexextra.$(OBJEXT) $(am__objects_56) -am__objects_57 = ptex-ptexini.$(OBJEXT) ptex-ptex0.$(OBJEXT) -nodist_ptex_OBJECTS = $(am__objects_57) ptex-ptex-pool.$(OBJEXT) +@PTEX_SYNCTEX_TRUE@am__objects_58 = synctexdir/ptex-synctex.$(OBJEXT) +dist_ptex_OBJECTS = ptexdir/ptex-ptexextra.$(OBJEXT) $(am__objects_58) +am__objects_59 = ptex-ptexini.$(OBJEXT) ptex-ptex0.$(OBJEXT) +nodist_ptex_OBJECTS = $(am__objects_59) ptex-ptex-pool.$(OBJEXT) ptex_OBJECTS = $(dist_ptex_OBJECTS) $(nodist_ptex_OBJECTS) @PTEX_SYNCTEX_TRUE@am__DEPENDENCIES_12 = $(am__DEPENDENCIES_1) am_ptftopl_OBJECTS = @@ -1594,10 +1614,10 @@ tangleboot_LDADD = $(LDADD) am__dist_tex_SOURCES_DIST = texextra.c synctexdir/synctex.c \ synctexdir/synctex.h synctexdir/synctex-common.h \ synctexdir/synctex-tex.h -@TEX_SYNCTEX_TRUE@am__objects_58 = synctexdir/tex-synctex.$(OBJEXT) -dist_tex_OBJECTS = tex-texextra.$(OBJEXT) $(am__objects_58) -am__objects_59 = tex-texini.$(OBJEXT) tex-tex0.$(OBJEXT) -nodist_tex_OBJECTS = $(am__objects_59) tex-tex-pool.$(OBJEXT) +@TEX_SYNCTEX_TRUE@am__objects_60 = synctexdir/tex-synctex.$(OBJEXT) +dist_tex_OBJECTS = tex-texextra.$(OBJEXT) $(am__objects_60) +am__objects_61 = tex-texini.$(OBJEXT) tex-tex0.$(OBJEXT) +nodist_tex_OBJECTS = $(am__objects_61) tex-tex-pool.$(OBJEXT) tex_OBJECTS = $(dist_tex_OBJECTS) $(nodist_tex_OBJECTS) @TEX_SYNCTEX_TRUE@am__DEPENDENCIES_14 = $(am__DEPENDENCIES_1) tex_DEPENDENCIES = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_1) \ @@ -1622,21 +1642,21 @@ am_updvitype_OBJECTS = nodist_updvitype_OBJECTS = updvitype-updvitype.$(OBJEXT) updvitype_OBJECTS = $(am_updvitype_OBJECTS) \ $(nodist_updvitype_OBJECTS) -am__objects_60 = upmpost-pmp.$(OBJEXT) -am__objects_61 = upmpost-pmpmath.$(OBJEXT) -am__objects_62 = upmpost-pmpmathbinary.$(OBJEXT) -am__objects_63 = upmpost-pmpmathdecimal.$(OBJEXT) -am__objects_64 = upmpost-pmpmathdouble.$(OBJEXT) -am__objects_65 = upmpost-pmpstrings.$(OBJEXT) -am__objects_66 = upmpost-pmpxout.$(OBJEXT) -am__objects_67 = upmpost-ppngout.$(OBJEXT) -am__objects_68 = upmpost-ppsout.$(OBJEXT) -am__objects_69 = upmpost-psvgout.$(OBJEXT) -am__objects_70 = $(am__objects_60) $(am__objects_61) $(am__objects_62) \ - $(am__objects_63) $(am__objects_64) upmpost-pmpost.$(OBJEXT) \ - $(am__objects_65) $(am__objects_66) $(am__objects_67) \ - $(am__objects_68) $(am__objects_69) upmpost-ptfmin.$(OBJEXT) -nodist_upmpost_OBJECTS = $(am__objects_70) +am__objects_62 = upmpost-pmp.$(OBJEXT) +am__objects_63 = upmpost-pmpmath.$(OBJEXT) +am__objects_64 = upmpost-pmpmathbinary.$(OBJEXT) +am__objects_65 = upmpost-pmpmathdecimal.$(OBJEXT) +am__objects_66 = upmpost-pmpmathdouble.$(OBJEXT) +am__objects_67 = upmpost-pmpstrings.$(OBJEXT) +am__objects_68 = upmpost-pmpxout.$(OBJEXT) +am__objects_69 = upmpost-ppngout.$(OBJEXT) +am__objects_70 = upmpost-ppsout.$(OBJEXT) +am__objects_71 = upmpost-psvgout.$(OBJEXT) +am__objects_72 = $(am__objects_62) $(am__objects_63) $(am__objects_64) \ + $(am__objects_65) $(am__objects_66) upmpost-pmpost.$(OBJEXT) \ + $(am__objects_67) $(am__objects_68) $(am__objects_69) \ + $(am__objects_70) $(am__objects_71) upmpost-ptfmin.$(OBJEXT) +nodist_upmpost_OBJECTS = $(am__objects_72) upmpost_OBJECTS = $(nodist_upmpost_OBJECTS) am__DEPENDENCIES_16 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) \ @@ -1650,12 +1670,12 @@ am__dist_uptex_SOURCES_DIST = uptexdir/uptexextra.c \ uptexdir/uptexextra.h uptexdir/uptex_version.h \ synctexdir/synctex.c synctexdir/synctex.h \ synctexdir/synctex-common.h synctexdir/synctex-uptex.h -@UPTEX_SYNCTEX_TRUE@am__objects_71 = \ +@UPTEX_SYNCTEX_TRUE@am__objects_73 = \ @UPTEX_SYNCTEX_TRUE@ synctexdir/uptex-synctex.$(OBJEXT) dist_uptex_OBJECTS = uptexdir/uptex-uptexextra.$(OBJEXT) \ - $(am__objects_71) -am__objects_72 = uptex-uptexini.$(OBJEXT) uptex-uptex0.$(OBJEXT) -nodist_uptex_OBJECTS = $(am__objects_72) uptex-uptex-pool.$(OBJEXT) + $(am__objects_73) +am__objects_74 = uptex-uptexini.$(OBJEXT) uptex-uptex0.$(OBJEXT) +nodist_uptex_OBJECTS = $(am__objects_74) uptex-uptex-pool.$(OBJEXT) uptex_OBJECTS = $(dist_uptex_OBJECTS) $(nodist_uptex_OBJECTS) @UPTEX_SYNCTEX_TRUE@am__DEPENDENCIES_17 = $(am__DEPENDENCIES_1) am_uptftopl_OBJECTS = @@ -1694,12 +1714,12 @@ am__dist_xetex_SOURCES_DIST = xetexdir/xetexextra.c \ xetexdir/xetex_version.h synctexdir/synctex.c \ synctexdir/synctex.h synctexdir/synctex-common.h \ synctexdir/synctex-xetex.h -@XETEX_SYNCTEX_TRUE@am__objects_73 = \ +@XETEX_SYNCTEX_TRUE@am__objects_75 = \ @XETEX_SYNCTEX_TRUE@ synctexdir/xetex-synctex.$(OBJEXT) dist_xetex_OBJECTS = xetexdir/xetex-xetexextra.$(OBJEXT) \ - $(am__objects_73) -am__objects_74 = xetex-xetexini.$(OBJEXT) xetex-xetex0.$(OBJEXT) -nodist_xetex_OBJECTS = $(am__objects_74) xetex-xetex-pool.$(OBJEXT) + $(am__objects_75) +am__objects_76 = xetex-xetexini.$(OBJEXT) xetex-xetex0.$(OBJEXT) +nodist_xetex_OBJECTS = $(am__objects_76) xetex-xetex-pool.$(OBJEXT) xetex_OBJECTS = $(dist_xetex_OBJECTS) $(nodist_xetex_OBJECTS) @XETEX_MACOSX_FALSE@am__DEPENDENCIES_18 = $(am__DEPENDENCIES_1) am__DEPENDENCIES_19 = $(libxetex) $(am__DEPENDENCIES_1) \ @@ -1750,7 +1770,15 @@ am__depfiles_remade = ./$(DEPDIR)/aleph-aleph-pool.Po \ ./$(DEPDIR)/euptex-euptex-pool.Po \ ./$(DEPDIR)/euptex-euptex0.Po ./$(DEPDIR)/euptex-euptexini.Po \ ./$(DEPDIR)/gftodvi.Po ./$(DEPDIR)/gftopk.Po \ - ./$(DEPDIR)/gftype.Po ./$(DEPDIR)/initex-callexe.Po \ + ./$(DEPDIR)/gftype.Po ./$(DEPDIR)/hishrink-hformat.Po \ + ./$(DEPDIR)/hishrink-hishrink.Po \ + ./$(DEPDIR)/hishrink-shrink-lexer.Po \ + ./$(DEPDIR)/hishrink-shrink-parser.Po \ + ./$(DEPDIR)/histretch-hformat.Po \ + ./$(DEPDIR)/histretch-histretch.Po \ + ./$(DEPDIR)/hitex-hformat.Po ./$(DEPDIR)/hitex-hitex.Po \ + ./$(DEPDIR)/hitex-hput.Po ./$(DEPDIR)/hitex-htex.Po \ + ./$(DEPDIR)/initex-callexe.Po \ ./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po \ ./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po \ ./$(DEPDIR)/libluahbtexspecific_a-printing-hb.Po \ @@ -2433,6 +2461,15 @@ AM_V_CXXLD = $(am__v_CXXLD_@AM_V@) am__v_CXXLD_ = $(am__v_CXXLD_@AM_DEFAULT_V@) am__v_CXXLD_0 = @echo " CXXLD " $@; am__v_CXXLD_1 = +@MAINTAINER_MODE_FALSE@am__skiplex = test -f $@ || +LEXCOMPILE = $(LEX) $(AM_LFLAGS) $(LFLAGS) +LTLEXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(LEX) $(AM_LFLAGS) $(LFLAGS) +AM_V_LEX = $(am__v_LEX_@AM_V@) +am__v_LEX_ = $(am__v_LEX_@AM_DEFAULT_V@) +am__v_LEX_0 = @echo " LEX " $@; +am__v_LEX_1 = +YLWRAP = $(top_srcdir)/../../build-aux/ylwrap OBJCXXCOMPILE = $(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) \ $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_OBJCXXFLAGS) $(OBJCXXFLAGS) LTOBJCXXCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ @@ -2451,6 +2488,16 @@ AM_V_OBJCXXLD = $(am__v_OBJCXXLD_@AM_V@) am__v_OBJCXXLD_ = $(am__v_OBJCXXLD_@AM_DEFAULT_V@) am__v_OBJCXXLD_0 = @echo " OBJCXXLD" $@; am__v_OBJCXXLD_1 = +@MAINTAINER_MODE_FALSE@am__skipyacc = test -f $@ || +am__yacc_c2h = sed -e s/cc$$/hh/ -e s/cpp$$/hpp/ -e s/cxx$$/hxx/ \ + -e s/c++$$/h++/ -e s/c$$/h/ +YACCCOMPILE = $(YACC) $(AM_YFLAGS) $(YFLAGS) +LTYACCCOMPILE = $(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) \ + $(LIBTOOLFLAGS) --mode=compile $(YACC) $(AM_YFLAGS) $(YFLAGS) +AM_V_YACC = $(am__v_YACC_@AM_V@) +am__v_YACC_ = $(am__v_YACC_@AM_DEFAULT_V@) +am__v_YACC_0 = @echo " YACC " $@; +am__v_YACC_1 = SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \ $(nodist_libluaffi_a_SOURCES) $(libluaharfbuzz_a_SOURCES) \ $(nodist_libluahbtexspecific_a_SOURCES) \ @@ -2493,8 +2540,10 @@ SOURCES = $(libff_a_SOURCES) $(libkanji_a_SOURCES) \ $(dist_euptex_SOURCES) $(nodist_euptex_SOURCES) \ $(gftodvi_SOURCES) $(nodist_gftodvi_SOURCES) \ $(nodist_gftopk_SOURCES) $(nodist_gftype_SOURCES) \ - $(nodist_initex_SOURCES) $(nodist_luahbtex_SOURCES) \ - $(nodist_EXTRA_luahbtex_SOURCES) $(nodist_luajithbtex_SOURCES) \ + $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \ + $(nodist_hitex_SOURCES) $(nodist_initex_SOURCES) \ + $(nodist_luahbtex_SOURCES) $(nodist_EXTRA_luahbtex_SOURCES) \ + $(nodist_luajithbtex_SOURCES) \ $(nodist_EXTRA_luajithbtex_SOURCES) \ $(nodist_luajittex_SOURCES) $(nodist_luatex_SOURCES) \ $(md5main_SOURCES) $(dist_mf_SOURCES) $(dist_mf_nowin_SOURCES) \ @@ -2755,14 +2804,14 @@ am__set_TESTS_bases = \ bases=`echo $$bases` AM_TESTSUITE_SUMMARY_HEADER = ' for $(PACKAGE_STRING)' RECHECK_LOGS = $(TEST_LOGS) -am__EXEEXT_43 = bibtex.test dvicopy.test dvitype.test gftodvi.test \ +am__EXEEXT_44 = bibtex.test dvicopy.test dvitype.test gftodvi.test \ gftopk.test gftype.test mft.test patgen.test pktogf.test \ pktype.test pltotf.test pooltype.test tftopl.test vftovp.test \ vptovf.test weave.test -am__EXEEXT_44 = $(am__EXEEXT_43) tests/bibtex-openout-test.pl \ +am__EXEEXT_45 = $(am__EXEEXT_44) 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_45 = $(am__EXEEXT_44) +@WEB_TRUE@am__EXEEXT_46 = $(am__EXEEXT_45) TEST_SUITE_LOG = test-suite.log am__test_logs1 = $(TESTS:=.log) am__test_logs2 = $(am__test_logs1:@EXEEXT@.log=.log) @@ -2790,7 +2839,8 @@ am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/../../am/bin_links.am \ $(srcdir)/am/cweb.am $(srcdir)/am/texmf.am $(srcdir)/am/web.am \ $(srcdir)/c-auto.in $(srcdir)/ctangleboot-sh.in \ $(srcdir)/eptexdir/am/eptex.am $(srcdir)/etexdir/am/etex.am \ - $(srcdir)/euptexdir/am/euptex.am $(srcdir)/libmd5/am/md5.am \ + $(srcdir)/euptexdir/am/euptex.am \ + $(srcdir)/hitexdir/am/hitex.am $(srcdir)/libmd5/am/md5.am \ $(srcdir)/luatexdir/am/libluatex.am \ $(srcdir)/luatexdir/am/libunilib.am \ $(srcdir)/luatexdir/am/luaffi.am \ @@ -2822,6 +2872,7 @@ am__DIST_COMMON = $(dist_man_MANS) $(srcdir)/../../am/bin_links.am \ $(top_srcdir)/../../build-aux/ltmain.sh \ $(top_srcdir)/../../build-aux/missing \ $(top_srcdir)/../../build-aux/test-driver \ + $(top_srcdir)/../../build-aux/ylwrap \ $(top_srcdir)/../texlive/w32_wrapper/callexe.c \ $(top_srcdir)/luatexdir/luafontloader/ff-config.in \ $(top_srcdir)/synctexdir/synctex.pc.in ../../build-aux/ar-lib \ @@ -2985,7 +3036,7 @@ NM = @NM@ NMEDIT = @NMEDIT@ OBJCXX = @OBJCXX@ OBJCXXDEPMODE = @OBJCXXDEPMODE@ -OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_110) +OBJCXXFLAGS = @OBJCXXFLAGS@ $(am__append_111) OBJDUMP = @OBJDUMP@ OBJEXT = @OBJEXT@ OTANGLE = @OTANGLE@ @@ -3127,8 +3178,8 @@ EXTRA_LIBRARIES = libmf.a libmflua.a libmfluaotfcc.a libmfluaotfcc.a \ libluajithbtexspecific.a libluaffi.a libluaharfbuzz.a \ libluajitharfbuzz.a libxetex.a libsynctex.a libmd5.a EXTRA_LTLIBRARIES = libsynctex.la -lib_LIBRARIES = $(am__append_122) -lib_LTLIBRARIES = $(am__append_121) +lib_LIBRARIES = $(am__append_123) +lib_LTLIBRARIES = $(am__append_122) dist_man_MANS = synctexdir/man1/synctex.1 synctexdir/man5/synctex.5 nodist_man_MANS = TEST_EXTENSIONS = .pl .test @@ -3245,21 +3296,21 @@ EXTRA_DIST = PROJECTS cftests cpascal.h help.h w2c/config.h \ euptexdir/euptrip/euptrip.diffs euptexdir/euptrip/texmf.cnf \ euptexdir/pdfprimitive.test \ eptexdir/tests/pdfprimitive-test.tex \ - euptexdir/tests/pdfprimitive-euptex.log \ - 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/pdfprimitive-euptex.log hitexdir/ChangeLog \ + $(hitex_web) lex.sed yacc.sed 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 \ @@ -3568,7 +3619,10 @@ DISTCLEANFILES = CXXLD.sh tangle.c tangle.h tangle.p tangle-web2c \ uptests/yuparse.* uptests/ygkhuge*.* uptrip.diffs \ $(nodist_euptex_SOURCES) euptex.web euptex.ch euptex-web2c \ euptex.p euptex.pool euptex-tangle euptrip.diffs \ - pdfprimitive-euptex.* $(nodist_pdftex_SOURCES) pdftex-final.ch \ + pdfprimitive-euptex.* $(nodist_hitex_SOURCES) \ + $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) \ + format-tangle htex-tangle hitex-tangle shrink-parser.* \ + shrink-lexer.* $(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 postV3.afm \ @@ -3607,16 +3661,16 @@ TRIPTRAP = $(am__append_7) $(am__append_17) $(am__append_26) \ $(am__append_79) bin_links = $(am__append_5) $(am__append_14) $(am__append_15) \ $(am__append_24) $(am__append_33) $(am__append_41) \ - $(am__append_49) $(am__append_54) $(am__append_88) \ - $(am__append_93) $(am__append_98) $(am__append_103) + $(am__append_49) $(am__append_54) $(am__append_89) \ + $(am__append_94) $(am__append_99) $(am__append_104) install_exe_links = $(am__append_12) $(am__append_22) $(am__append_31) \ $(am__append_39) $(am__append_47) $(am__append_52) \ - $(am__append_86) $(am__append_91) $(am__append_96) \ - $(am__append_101) + $(am__append_87) $(am__append_92) $(am__append_97) \ + $(am__append_102) uninstall_exe_links = $(am__append_13) $(am__append_23) \ $(am__append_32) $(am__append_40) $(am__append_48) \ - $(am__append_53) $(am__append_87) $(am__append_92) \ - $(am__append_97) $(am__append_102) + $(am__append_53) $(am__append_88) $(am__append_93) \ + $(am__append_98) $(am__append_103) NEVER_DIST = `find . $(NEVER_NAMES)` cwebdir/cweave.log \ cwebdir/cweave.trs cwebdir/ctwill.log cwebdir/ctwill.trs \ cwebdir/refsort.log cwebdir/refsort.trs cwebdir/twinx.log \ @@ -3787,18 +3841,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_125) +tex_CPPFLAGS = $(AM_CPPFLAGS) $(am__append_126) # With --enable-ipc, TeX may need to link with -lsocket. -tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_126) +tex_LDADD = $(LDADD) $(ipc_socketlibs) $(am__append_127) # 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_128) +dist_tex_SOURCES = texextra.c $(am__append_129) # We must create texd.h before building the tex_OBJECTS. -tex_prereq = texd.h $(am__append_127) +tex_prereq = texd.h $(am__append_128) tex_ch_srcs = \ tex.web \ tex.ch \ @@ -4274,21 +4328,21 @@ libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w \ mplibdir/pngout.w mplibdir/mpmath.w mplibdir/mpmathbinary.w \ mplibdir/mpmathdecimal.w mplibdir/mpmathdouble.w \ mplibdir/mpstrings.w mplibdir/tfmin.w -etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_129) +etex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(am__append_130) # With --enable-ipc, e-TeX may need to link with -lsocket. -etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_130) +etex_LDADD = $(LDADD) $(ZLIB_LIBS) $(ipc_socketlibs) $(am__append_131) 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_132) + etexdir/etex_version.h $(am__append_133) # 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_131) +etex_prereq = etexd.h etexdir/etex_version.h $(am__append_132) etex_web_srcs = \ tex.web \ etexdir/etex.ch @@ -4317,20 +4371,20 @@ pweb_programs = pbibtex pdvitype ppltotf ptftopl pproglib = lib/libp.a libkanji_a_SOURCES = ptexdir/kanji.c ptexdir/kanji.h ptexdir/kanji_dump.c libkanji_a_CPPFLAGS = $(ptex_cppflags) -ptex_CPPFLAGS = $(ptex_cppflags) $(am__append_133) +ptex_CPPFLAGS = $(ptex_cppflags) $(am__append_134) # With --enable-ipc, pTeX may need to link with -lsocket. -ptex_LDADD = $(ptex_ldadd) $(ipc_socketlibs) $(am__append_134) +ptex_LDADD = $(ptex_ldadd) $(ipc_socketlibs) $(am__append_135) ptex_DEPENDENCIES = $(ptex_dependencies) # pTeX C sources ptex_c_h = ptexini.c ptex0.c ptexcoerce.h ptexd.h nodist_ptex_SOURCES = $(ptex_c_h) ptex-pool.c dist_ptex_SOURCES = ptexdir/ptexextra.c ptexdir/ptexextra.h \ - ptexdir/ptex_version.h $(am__append_136) + ptexdir/ptex_version.h $(am__append_137) # We must create ptexd.h and ptexdir/ptex_version.h before building the ptex_OBJECTS. -ptex_prereq = ptexd.h ptexdir/ptex_version.h $(am__append_135) +ptex_prereq = ptexd.h ptexdir/ptex_version.h $(am__append_136) ptex_web_srcs = \ tex.web \ tex.ch \ @@ -4374,22 +4428,22 @@ pweb_tests = ptexdir/pbibtex.test ptexdir/pdvitype.test \ ptexdir/nissya.test ptexdir/sample.test ptexdir/yokotate.test \ ptexdir/skipjfmp.test eptex_CPPFLAGS = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/libmd5 \ - $(ZLIB_INCLUDES) $(am__append_141) + $(ZLIB_INCLUDES) $(am__append_142) # With --enable-ipc, e-pTeX may need to link with -lsocket. eptex_LDADD = libkanji.a $(pproglib) $(PTEXENC_LIBS) $(LDADD) \ - $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_142) + $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_143) eptex_DEPENDENCIES = libkanji.a $(pproglib) $(PTEXENC_DEPEND) $(default_dependencies) libmd5.a $(ZLIB_DEPEND) # e-pTeX C sources eptex_c_h = eptexini.c eptex0.c eptexcoerce.h eptexd.h nodist_eptex_SOURCES = $(eptex_c_h) eptex-pool.c dist_eptex_SOURCES = eptexdir/eptexextra.c eptexdir/eptexextra.h \ - eptexdir/eptex_version.h $(am__append_144) + eptexdir/eptex_version.h $(am__append_145) # We must create eptexd.h and eptexdir/eptex_version.h before building the eptex_OBJECTS. eptex_prereq = eptexd.h etexdir/etex_version.h ptexdir/ptex_version.h \ - eptexdir/eptex_version.h $(am__append_143) + eptexdir/eptex_version.h $(am__append_144) eptex_web_srcs = \ tex.web \ etexdir/etex.ch \ @@ -4423,21 +4477,21 @@ up_tangle = $(tangle_silent)WEBINPUTS=.:$(srcdir)/uptexdir:$(srcdir) $(buildenv) upweb_programs = upbibtex updvitype uppltotf uptftopl libukanji_a_SOURCES = uptexdir/kanji.c uptexdir/kanji.h uptexdir/kanji_dump.c libukanji_a_CPPFLAGS = $(uptex_cppflags) -uptex_CPPFLAGS = $(uptex_cppflags) $(am__append_137) +uptex_CPPFLAGS = $(uptex_cppflags) $(am__append_138) # With --enable-ipc, upTeX may need to link with -lsocket. -uptex_LDADD = $(uptex_ldadd) $(ipc_socketlibs) $(am__append_138) +uptex_LDADD = $(uptex_ldadd) $(ipc_socketlibs) $(am__append_139) uptex_DEPENDENCIES = $(uptex_dependencies) # upTeX C sources uptex_c_h = uptexini.c uptex0.c uptexcoerce.h uptexd.h nodist_uptex_SOURCES = $(uptex_c_h) uptex-pool.c dist_uptex_SOURCES = uptexdir/uptexextra.c uptexdir/uptexextra.h \ - uptexdir/uptex_version.h $(am__append_140) + uptexdir/uptex_version.h $(am__append_141) # We must create uptexd.h and uptexdir/uptex_version.h before building the uptex_OBJECTS. uptex_prereq = uptexd.h ptexdir/ptex_version.h \ - uptexdir/uptex_version.h $(am__append_139) + uptexdir/uptex_version.h $(am__append_140) uptex_web_srcs = \ tex.web \ tex.ch \ @@ -4485,23 +4539,23 @@ upweb_tests = \ uptexdir/gkhuge.test euptex_CPPFLAGS = $(PTEXENC_INCLUDES) $(AM_CPPFLAGS) \ - -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_145) + -I$(srcdir)/libmd5 $(ZLIB_INCLUDES) $(am__append_146) # 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_146) + $(ipc_socketlibs) libmd5.a $(ZLIB_LIBS) $(am__append_147) 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_148) + $(am__append_149) # 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 \ ptexdir/ptex_version.h eptexdir/eptex_version.h \ - uptexdir/uptex_version.h $(am__append_147) + uptexdir/uptex_version.h $(am__append_148) euptex_web_srcs = \ tex.web \ etexdir/etex.ch \ @@ -4532,6 +4586,47 @@ euptex_ch_srcs = \ # e-upTeX Tests # euptex_tests = euptexdir/euptriptest.test euptexdir/pdfprimitive.test euptexdir/eupver.test + +#kb hitex_CFLAGS = $(AM_CFLAGS) \ +#kb -Wno-parentheses -Wno-maybe-uninitialized -Wno-unused-function -Wno-unused-variable +hitex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) +hitex_LDADD = $(KPATHSEA_LIBS) $(ZLIB_LIBS) +# +histretch_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) +histretch_LDADD = $(ZLIB_LIBS) +# +hishrink_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) +hishrink_LDADD = $(ZLIB_LIBS) + +# We want debugging for the parser, and we use features not in POSIX yacc. +# I guess the result is that we assume bison? Not sure if that's ok. +AM_YFLAGS = -d -v -Wno-yacc + +# HiTeX CWEB sources +hitex_web = hitexdir/format.w hitexdir/htex.w hitexdir/hitex.w + +# Creating several files: need stamp file and two rules with identical recipes +hi_ctangle_sh = CWEBINPUTS=$(srcdir)/hitexdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE) + +# HiTeX C/yacc/lex sources generated using ctangle. +# For each cweb (.w) source file, list the files generated. +format_c_h_l_y = basetypes.h error.h hformat.h \ + mkhformat.c \ + hput.c hput.h \ + hget.c hget.h \ + shrink.lex-in shrink.yacc-in \ + hishrink.c histretch.c + +htex_c_h = htex.c htex.h +hitex_c_h = hitex.c hitex.h + +# Other web2c/*dir/am/*.am engine Makefile fragments define a bunch of +# _OBJECTS variables. This does not seem right to me. Instead, we +# correctly define the _SOURCES. +# +nodist_hitex_SOURCES = hformat.c hput.c $(htex_c_h) $(hitex_c_h) +nodist_histretch_SOURCES = hformat.c histretch.c +nodist_hishrink_SOURCES = hformat.c hishrink.c shrink-lexer.l shrink-parser.y libpdftex_a_CPPFLAGS = $(REGEX_INCLUDES) $(pdftex_cppflags) libpdftex_a_CXXFLAGS = $(WARNING_CXXFLAGS) libpdftex_a_SOURCES = pdftexdir/avl.c pdftexdir/avl.h \ @@ -4545,7 +4640,7 @@ libpdftex_a_SOURCES = pdftexdir/avl.c pdftexdir/avl.h \ pdftexdir/writejbig2.c pdftexdir/writejpg.c \ pdftexdir/writepng.c pdftexdir/writet1.c pdftexdir/writet3.c \ pdftexdir/writettf.c pdftexdir/writettf.h pdftexdir/writezip.c \ - $(am__append_81) + $(am__append_82) @MINGW32_TRUE@REGEX_INCLUDES = -I$(srcdir)/pdftexdir/regex EXTRA_libpdftex_a_SOURCES = pdftexdir/macnames.c \ pdftexdir/regex/regcomp.c pdftexdir/regex/regex_internal.c \ @@ -4561,7 +4656,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_149) +pdftex_CPPFLAGS = $(pdftex_cppflags) $(am__append_150) pdftex_CXXFLAGS = $(WARNING_CXXFLAGS) # With --enable-ipc, pdfTeX may need to link with -lsocket. @@ -4574,7 +4669,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_150) + $(am__append_151) pdftex_ch_srcs = \ pdftexdir/pdftex.web \ pdftexdir/tex.ch0 \ @@ -5248,18 +5343,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_109) \ - $(am__append_111) + $(PPLIB_INCLUDES) -I$(srcdir)/libmd5 $(am__append_110) \ + $(am__append_112) 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_112) + $(am__append_113) 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_151) +xetex_CPPFLAGS = $(xetex_cppflags) $(am__append_152) xetex_CFLAGS = $(WARNING_CFLAGS) xetex_CXXFLAGS = # $(WARNING_CXXFLAGS) xetex_LDADD = $(xetex_ldadd) $(LDADD) $(ipc_socketlibs) @@ -5268,7 +5363,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_152) + $(am__append_153) xetex_ch_srcs = \ xetexdir/xetex.web \ xetexdir/tex.ch0 \ @@ -5299,7 +5394,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_113) $(am__append_114) + xetexdir/image/pngimage.h $(am__append_114) $(am__append_115) # We must create xetexd.h etc. before building the libxetex_a_OBJECTS. libxetex_prereq = xetexd.h $(xetex_dependencies) @@ -5404,12 +5499,12 @@ synctex_SOURCES = \ synctexdir/synctex_main.c synctex_CPPFLAGS = -I$(srcdir)/synctexdir -synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_123) +synctex_LDADD = $(libsynctex) $(ZLIB_LIBS) $(am__append_124) 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_124) +libsynctex_la_LIBADD = $(ZLIB_LIBS) $(am__append_125) libsynctex_la_SOURCES = \ synctexdir/synctex_parser.c \ synctexdir/synctex_parser_local.h \ @@ -5523,10 +5618,10 @@ all: ff-config.h $(MAKE) $(AM_MAKEFLAGS) all-recursive .SUFFIXES: -.SUFFIXES: .c .cc .cin .cpp .cxx .h .hin .lo .log .mm .o .obj .p .pin .pl .pl$(EXEEXT) .test .test$(EXEEXT) .trs +.SUFFIXES: .c .cc .cin .cpp .cxx .h .hin .l .lo .log .mm .o .obj .p .pin .pl .pl$(EXEEXT) .test .test$(EXEEXT) .trs .y 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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.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) @for dep in $?; do \ case '$(am__configure_deps)' in \ *$$dep*) \ @@ -5548,7 +5643,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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.am $(srcdir)/uptexdir/am/uptex.am $(srcdir)/euptexdir/am/euptex.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)/mfluajitdir/am/mfluajit.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)/eptexdir/am/eptex.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): $(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) $(SHELL) ./config.status --recheck @@ -7950,6 +8045,21 @@ gftopk$(EXEEXT): $(gftopk_OBJECTS) $(gftopk_DEPENDENCIES) $(EXTRA_gftopk_DEPENDE gftype$(EXEEXT): $(gftype_OBJECTS) $(gftype_DEPENDENCIES) $(EXTRA_gftype_DEPENDENCIES) @rm -f gftype$(EXEEXT) $(AM_V_CCLD)$(LINK) $(gftype_OBJECTS) $(gftype_LDADD) $(LIBS) +shrink-parser.h: shrink-parser.c + @if test ! -f $@; then rm -f shrink-parser.c; else :; fi + @if test ! -f $@; then $(MAKE) $(AM_MAKEFLAGS) shrink-parser.c; else :; fi + +hishrink$(EXEEXT): $(hishrink_OBJECTS) $(hishrink_DEPENDENCIES) $(EXTRA_hishrink_DEPENDENCIES) + @rm -f hishrink$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(hishrink_OBJECTS) $(hishrink_LDADD) $(LIBS) + +histretch$(EXEEXT): $(histretch_OBJECTS) $(histretch_DEPENDENCIES) $(EXTRA_histretch_DEPENDENCIES) + @rm -f histretch$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(histretch_OBJECTS) $(histretch_LDADD) $(LIBS) + +hitex$(EXEEXT): $(hitex_OBJECTS) $(hitex_DEPENDENCIES) $(EXTRA_hitex_DEPENDENCIES) + @rm -f hitex$(EXEEXT) + $(AM_V_CCLD)$(LINK) $(hitex_OBJECTS) $(hitex_LDADD) $(LIBS) initex$(EXEEXT): $(initex_OBJECTS) $(initex_DEPENDENCIES) $(EXTRA_initex_DEPENDENCIES) @rm -f initex$(EXEEXT) @@ -8331,6 +8441,16 @@ distclean-compile: @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gftodvi.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gftopk.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gftype.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-hformat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-hishrink.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-shrink-lexer.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hishrink-shrink-parser.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histretch-hformat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/histretch-histretch.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-hformat.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-hitex.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-hput.Po@am__quote@ # am--include-marker +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/hitex-htex.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/initex-callexe.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po@am__quote@ # am--include-marker @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po@am__quote@ # am--include-marker @@ -17248,6 +17368,146 @@ euptex-euptex-pool.obj: euptex-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) $(euptex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o euptex-euptex-pool.obj `if test -f 'euptex-pool.c'; then $(CYGPATH_W) 'euptex-pool.c'; else $(CYGPATH_W) '$(srcdir)/euptex-pool.c'; fi` +hishrink-hformat.o: hformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hformat.o -MD -MP -MF $(DEPDIR)/hishrink-hformat.Tpo -c -o hishrink-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hformat.Tpo $(DEPDIR)/hishrink-hformat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hishrink-hformat.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c + +hishrink-hformat.obj: hformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hformat.obj -MD -MP -MF $(DEPDIR)/hishrink-hformat.Tpo -c -o hishrink-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hformat.Tpo $(DEPDIR)/hishrink-hformat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hishrink-hformat.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi` + +hishrink-hishrink.o: hishrink.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hishrink.o -MD -MP -MF $(DEPDIR)/hishrink-hishrink.Tpo -c -o hishrink-hishrink.o `test -f 'hishrink.c' || echo '$(srcdir)/'`hishrink.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hishrink.Tpo $(DEPDIR)/hishrink-hishrink.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hishrink.c' object='hishrink-hishrink.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hishrink.o `test -f 'hishrink.c' || echo '$(srcdir)/'`hishrink.c + +hishrink-hishrink.obj: hishrink.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-hishrink.obj -MD -MP -MF $(DEPDIR)/hishrink-hishrink.Tpo -c -o hishrink-hishrink.obj `if test -f 'hishrink.c'; then $(CYGPATH_W) 'hishrink.c'; else $(CYGPATH_W) '$(srcdir)/hishrink.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-hishrink.Tpo $(DEPDIR)/hishrink-hishrink.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hishrink.c' object='hishrink-hishrink.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-hishrink.obj `if test -f 'hishrink.c'; then $(CYGPATH_W) 'hishrink.c'; else $(CYGPATH_W) '$(srcdir)/hishrink.c'; fi` + +hishrink-shrink-lexer.o: shrink-lexer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-lexer.o -MD -MP -MF $(DEPDIR)/hishrink-shrink-lexer.Tpo -c -o hishrink-shrink-lexer.o `test -f 'shrink-lexer.c' || echo '$(srcdir)/'`shrink-lexer.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-lexer.Tpo $(DEPDIR)/hishrink-shrink-lexer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-lexer.c' object='hishrink-shrink-lexer.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-lexer.o `test -f 'shrink-lexer.c' || echo '$(srcdir)/'`shrink-lexer.c + +hishrink-shrink-lexer.obj: shrink-lexer.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-lexer.obj -MD -MP -MF $(DEPDIR)/hishrink-shrink-lexer.Tpo -c -o hishrink-shrink-lexer.obj `if test -f 'shrink-lexer.c'; then $(CYGPATH_W) 'shrink-lexer.c'; else $(CYGPATH_W) '$(srcdir)/shrink-lexer.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-lexer.Tpo $(DEPDIR)/hishrink-shrink-lexer.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-lexer.c' object='hishrink-shrink-lexer.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-lexer.obj `if test -f 'shrink-lexer.c'; then $(CYGPATH_W) 'shrink-lexer.c'; else $(CYGPATH_W) '$(srcdir)/shrink-lexer.c'; fi` + +hishrink-shrink-parser.o: shrink-parser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-parser.o -MD -MP -MF $(DEPDIR)/hishrink-shrink-parser.Tpo -c -o hishrink-shrink-parser.o `test -f 'shrink-parser.c' || echo '$(srcdir)/'`shrink-parser.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-parser.Tpo $(DEPDIR)/hishrink-shrink-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-parser.c' object='hishrink-shrink-parser.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-parser.o `test -f 'shrink-parser.c' || echo '$(srcdir)/'`shrink-parser.c + +hishrink-shrink-parser.obj: shrink-parser.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hishrink-shrink-parser.obj -MD -MP -MF $(DEPDIR)/hishrink-shrink-parser.Tpo -c -o hishrink-shrink-parser.obj `if test -f 'shrink-parser.c'; then $(CYGPATH_W) 'shrink-parser.c'; else $(CYGPATH_W) '$(srcdir)/shrink-parser.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hishrink-shrink-parser.Tpo $(DEPDIR)/hishrink-shrink-parser.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='shrink-parser.c' object='hishrink-shrink-parser.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) $(hishrink_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hishrink-shrink-parser.obj `if test -f 'shrink-parser.c'; then $(CYGPATH_W) 'shrink-parser.c'; else $(CYGPATH_W) '$(srcdir)/shrink-parser.c'; fi` + +histretch-hformat.o: hformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-hformat.o -MD -MP -MF $(DEPDIR)/histretch-hformat.Tpo -c -o histretch-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-hformat.Tpo $(DEPDIR)/histretch-hformat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='histretch-hformat.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c + +histretch-hformat.obj: hformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-hformat.obj -MD -MP -MF $(DEPDIR)/histretch-hformat.Tpo -c -o histretch-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-hformat.Tpo $(DEPDIR)/histretch-hformat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='histretch-hformat.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi` + +histretch-histretch.o: histretch.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-histretch.o -MD -MP -MF $(DEPDIR)/histretch-histretch.Tpo -c -o histretch-histretch.o `test -f 'histretch.c' || echo '$(srcdir)/'`histretch.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-histretch.Tpo $(DEPDIR)/histretch-histretch.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='histretch.c' object='histretch-histretch.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-histretch.o `test -f 'histretch.c' || echo '$(srcdir)/'`histretch.c + +histretch-histretch.obj: histretch.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT histretch-histretch.obj -MD -MP -MF $(DEPDIR)/histretch-histretch.Tpo -c -o histretch-histretch.obj `if test -f 'histretch.c'; then $(CYGPATH_W) 'histretch.c'; else $(CYGPATH_W) '$(srcdir)/histretch.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/histretch-histretch.Tpo $(DEPDIR)/histretch-histretch.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='histretch.c' object='histretch-histretch.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) $(histretch_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o histretch-histretch.obj `if test -f 'histretch.c'; then $(CYGPATH_W) 'histretch.c'; else $(CYGPATH_W) '$(srcdir)/histretch.c'; fi` + +hitex-hformat.o: hformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hformat.o -MD -MP -MF $(DEPDIR)/hitex-hformat.Tpo -c -o hitex-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hformat.Tpo $(DEPDIR)/hitex-hformat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hitex-hformat.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hformat.o `test -f 'hformat.c' || echo '$(srcdir)/'`hformat.c + +hitex-hformat.obj: hformat.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hformat.obj -MD -MP -MF $(DEPDIR)/hitex-hformat.Tpo -c -o hitex-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hformat.Tpo $(DEPDIR)/hitex-hformat.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hformat.c' object='hitex-hformat.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hformat.obj `if test -f 'hformat.c'; then $(CYGPATH_W) 'hformat.c'; else $(CYGPATH_W) '$(srcdir)/hformat.c'; fi` + +hitex-hput.o: hput.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hput.o -MD -MP -MF $(DEPDIR)/hitex-hput.Tpo -c -o hitex-hput.o `test -f 'hput.c' || echo '$(srcdir)/'`hput.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hput.Tpo $(DEPDIR)/hitex-hput.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hput.c' object='hitex-hput.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hput.o `test -f 'hput.c' || echo '$(srcdir)/'`hput.c + +hitex-hput.obj: hput.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hput.obj -MD -MP -MF $(DEPDIR)/hitex-hput.Tpo -c -o hitex-hput.obj `if test -f 'hput.c'; then $(CYGPATH_W) 'hput.c'; else $(CYGPATH_W) '$(srcdir)/hput.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hput.Tpo $(DEPDIR)/hitex-hput.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hput.c' object='hitex-hput.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hput.obj `if test -f 'hput.c'; then $(CYGPATH_W) 'hput.c'; else $(CYGPATH_W) '$(srcdir)/hput.c'; fi` + +hitex-htex.o: htex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-htex.o -MD -MP -MF $(DEPDIR)/hitex-htex.Tpo -c -o hitex-htex.o `test -f 'htex.c' || echo '$(srcdir)/'`htex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-htex.Tpo $(DEPDIR)/hitex-htex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htex.c' object='hitex-htex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-htex.o `test -f 'htex.c' || echo '$(srcdir)/'`htex.c + +hitex-htex.obj: htex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-htex.obj -MD -MP -MF $(DEPDIR)/hitex-htex.Tpo -c -o hitex-htex.obj `if test -f 'htex.c'; then $(CYGPATH_W) 'htex.c'; else $(CYGPATH_W) '$(srcdir)/htex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-htex.Tpo $(DEPDIR)/hitex-htex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='htex.c' object='hitex-htex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-htex.obj `if test -f 'htex.c'; then $(CYGPATH_W) 'htex.c'; else $(CYGPATH_W) '$(srcdir)/htex.c'; fi` + +hitex-hitex.o: hitex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hitex.o -MD -MP -MF $(DEPDIR)/hitex-hitex.Tpo -c -o hitex-hitex.o `test -f 'hitex.c' || echo '$(srcdir)/'`hitex.c +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hitex.Tpo $(DEPDIR)/hitex-hitex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hitex.c' object='hitex-hitex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hitex.o `test -f 'hitex.c' || echo '$(srcdir)/'`hitex.c + +hitex-hitex.obj: hitex.c +@am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT hitex-hitex.obj -MD -MP -MF $(DEPDIR)/hitex-hitex.Tpo -c -o hitex-hitex.obj `if test -f 'hitex.c'; then $(CYGPATH_W) 'hitex.c'; else $(CYGPATH_W) '$(srcdir)/hitex.c'; fi` +@am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/hitex-hitex.Tpo $(DEPDIR)/hitex-hitex.Po +@AMDEP_TRUE@@am__fastdepCC_FALSE@ $(AM_V_CC)source='hitex.c' object='hitex-hitex.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) $(hitex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o hitex-hitex.obj `if test -f 'hitex.c'; then $(CYGPATH_W) 'hitex.c'; else $(CYGPATH_W) '$(srcdir)/hitex.c'; fi` + initex-callexe.o: callexe.c @am__fastdepCC_TRUE@ $(AM_V_CC)$(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(initex_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -MT initex-callexe.o -MD -MP -MF $(DEPDIR)/initex-callexe.Tpo -c -o initex-callexe.o `test -f 'callexe.c' || echo '$(srcdir)/'`callexe.c @am__fastdepCC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/initex-callexe.Tpo $(DEPDIR)/initex-callexe.Po @@ -18636,6 +18896,13 @@ xetex-dummy.obj: dummy.cxx @AMDEP_TRUE@@am__fastdepCXX_FALSE@ DEPDIR=$(DEPDIR) $(CXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepCXX_FALSE@ $(AM_V_CXX@am__nodep@)$(LTCXXCOMPILE) -c -o $@ $< +.l.c: + $(AM_V_LEX)$(am__skiplex) $(SHELL) $(YLWRAP) $< $(LEX_OUTPUT_ROOT).c $@ -- $(LEXCOMPILE) + +shrink-lexer.c: shrink-lexer.l + $(AM_V_LEX) \ + $(SHELL) $(YLWRAP) `test -f 'shrink-lexer.l' || echo '$(srcdir)/'`shrink-lexer.l $(LEX_OUTPUT_ROOT).c shrink-lexer.c -- $(LEX) $(AM_LFLAGS) $(LFLAGS) + .mm.o: @am__fastdepOBJCXX_TRUE@ $(AM_V_OBJCXX)depbase=`echo $@ | sed 's|[^/]*$$|$(DEPDIR)/&|;s|\.o$$||'`;\ @am__fastdepOBJCXX_TRUE@ $(OBJCXXCOMPILE) -MT $@ -MD -MP -MF $$depbase.Tpo -c -o $@ $< &&\ @@ -18674,6 +18941,13 @@ xetexdir/libxetex_a-XeTeXFontMgr_Mac.obj: xetexdir/XeTeXFontMgr_Mac.mm @AMDEP_TRUE@@am__fastdepOBJCXX_FALSE@ DEPDIR=$(DEPDIR) $(OBJCXXDEPMODE) $(depcomp) @AMDEPBACKSLASH@ @am__fastdepOBJCXX_FALSE@ $(AM_V_OBJCXX@am__nodep@)$(OBJCXX) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(libxetex_a_CPPFLAGS) $(CPPFLAGS) $(libxetex_a_OBJCXXFLAGS) $(OBJCXXFLAGS) -c -o xetexdir/libxetex_a-XeTeXFontMgr_Mac.obj `if test -f 'xetexdir/XeTeXFontMgr_Mac.mm'; then $(CYGPATH_W) 'xetexdir/XeTeXFontMgr_Mac.mm'; else $(CYGPATH_W) '$(srcdir)/xetexdir/XeTeXFontMgr_Mac.mm'; fi` +.y.c: + $(AM_V_YACC)$(am__skipyacc) $(SHELL) $(YLWRAP) $< y.tab.c $@ y.tab.h `echo $@ | $(am__yacc_c2h)` y.output $*.output -- $(YACCCOMPILE) + +shrink-parser.c: shrink-parser.y + $(AM_V_YACC) \ + $(SHELL) $(YLWRAP) `test -f 'shrink-parser.y' || echo '$(srcdir)/'`shrink-parser.y y.tab.c shrink-parser.c y.tab.h `echo shrink-parser.c | $(am__yacc_c2h)` y.output shrink-parser.output -- $(YACC) $(AM_YFLAGS) $(YFLAGS) + mostlyclean-libtool: -rm -f *.lo @@ -19324,6 +19598,9 @@ mostlyclean-generic: -test -z "$(TEST_SUITE_LOG)" || rm -f $(TEST_SUITE_LOG) clean-generic: + -rm -f shrink-lexer.c + -rm -f shrink-parser.c + -rm -f shrink-parser.h -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) distclean-generic: @@ -19511,6 +19788,16 @@ distclean: distclean-recursive -rm -f ./$(DEPDIR)/gftodvi.Po -rm -f ./$(DEPDIR)/gftopk.Po -rm -f ./$(DEPDIR)/gftype.Po + -rm -f ./$(DEPDIR)/hishrink-hformat.Po + -rm -f ./$(DEPDIR)/hishrink-hishrink.Po + -rm -f ./$(DEPDIR)/hishrink-shrink-lexer.Po + -rm -f ./$(DEPDIR)/hishrink-shrink-parser.Po + -rm -f ./$(DEPDIR)/histretch-hformat.Po + -rm -f ./$(DEPDIR)/histretch-histretch.Po + -rm -f ./$(DEPDIR)/hitex-hformat.Po + -rm -f ./$(DEPDIR)/hitex-hitex.Po + -rm -f ./$(DEPDIR)/hitex-hput.Po + -rm -f ./$(DEPDIR)/hitex-htex.Po -rm -f ./$(DEPDIR)/initex-callexe.Po -rm -f ./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po -rm -f ./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po @@ -20280,6 +20567,16 @@ maintainer-clean: maintainer-clean-recursive -rm -f ./$(DEPDIR)/gftodvi.Po -rm -f ./$(DEPDIR)/gftopk.Po -rm -f ./$(DEPDIR)/gftype.Po + -rm -f ./$(DEPDIR)/hishrink-hformat.Po + -rm -f ./$(DEPDIR)/hishrink-hishrink.Po + -rm -f ./$(DEPDIR)/hishrink-shrink-lexer.Po + -rm -f ./$(DEPDIR)/hishrink-shrink-parser.Po + -rm -f ./$(DEPDIR)/histretch-hformat.Po + -rm -f ./$(DEPDIR)/histretch-histretch.Po + -rm -f ./$(DEPDIR)/hitex-hformat.Po + -rm -f ./$(DEPDIR)/hitex-hitex.Po + -rm -f ./$(DEPDIR)/hitex-hput.Po + -rm -f ./$(DEPDIR)/hitex-htex.Po -rm -f ./$(DEPDIR)/initex-callexe.Po -rm -f ./$(DEPDIR)/libluahbtexspecific_a-luainit-hb.Po -rm -f ./$(DEPDIR)/libluahbtexspecific_a-luastuff-hb.Po @@ -21825,6 +22122,48 @@ euptrip.diffs: euptex$(EXEEXT) dvitype$(EXEEXT) pltotf$(EXEEXT) tftopl$(EXEEXT) euptrip-clean: rm -rf euptripdir +$(format_c_h_l_y): format-tangle + $(hi_ctangle_sh) format +# +format-tangle: ctangle$(EXEEXT) hitexdir/format.w tangle-sh + $(hi_ctangle_sh) format + +$(hitex_c_h): hitex-tangle + $(hi_ctangle_sh) hitex +# +hitex-tangle: ctangle$(EXEEXT) hitexdir/hitex.w tangle-sh + $(hi_ctangle_sh) hitex + +# htex.c needs hitex.h, so depend on the hitex tangle. +$(htex_c_h): htex-tangle hitex-tangle + $(hi_ctangle_sh) htex +# +htex-tangle: ctangle$(EXEEXT) hitexdir/htex.w tangle-sh hitex-tangle + $(hi_ctangle_sh) htex + +# Generating hformat.c using mkhformat. +hformat.c: mkhformat + ./mkhformat >hformat.c || { rm -f hformat.c; exit 1; } + +# Postprocessing lex and yacc files generated by ctangle. +# (SED is defined with AC_PROG_SED) +shrink-parser.y: $(srcdir)/hitexdir/yacc.sed shrink.yacc-in + $(SED) -f $(srcdir)/hitexdir/yacc.sed shrink.yacc-in >shrink-parser.y \ + || { rm -f shrink-parser.y; exit 1; } +# +shrink-lexer.l: $(srcdir)/hitexdir/lex.sed shrink.lex-in + $(SED) -f $(srcdir)/hitexdir/lex.sed shrink.lex-in >shrink-lexer.l \ + || { rm -f shrink-lexer.l; exit 1; } + +# shrink needs the yacc header. It's actually the .o that should be the +# target here, seems to me, but hishrink.o is not used for the object +# name, it's hishrink-hishrink.o (because the program is also named +# hishrink), and adding that here ends up trying to link with +# hishrink-hishrink.o (I don't know), which doesn't exist. Sigh. +hishrink.c: shrink-parser.h shrink-parser.c + +# still missing + # We must create pdftexd.h (and xpdf...) before building the libpdftex_a_OBJECTS. $(libpdftex_a_OBJECTS): pdftexd.h $(XPDF_DEPEND) $(ZLIB_DEPEND) $(LIBPNG_DEPEND) $(pdftex_OBJECTS): $(pdftex_prereq) diff --git a/Build/source/texk/web2c/ac/web2c.ac b/Build/source/texk/web2c/ac/web2c.ac index 40afc44f3d6..aec20a703c5 100644 --- a/Build/source/texk/web2c/ac/web2c.ac +++ b/Build/source/texk/web2c/ac/web2c.ac @@ -1,7 +1,7 @@ dnl $Id$ # texk/web2c/ac/web2c.ac: configure.ac fragment for TL subdir dnl -dnl Copyright 2015-2020 Karl Berry +dnl Copyright 2015-2021 Karl Berry dnl Copyright 2009-2015 Peter Breitenlohner dnl You may freely use, modify and/or distribute this file. dnl @@ -36,6 +36,7 @@ m4_define([kpse_tex_progs], [dnl [[uptex], [yes], [yes], [upTeX], [ptexenc zlib]], [[euptex], [yes], [yes], [e-upTeX], [ptexenc zlib]], [[aleph], [yes], [], [Aleph], []], +[[hitex], [no], [], [HiTeX], [zlib]], [[pdftex], [yes], [yes], [pdfTeX], [xpdf libpng zlib]], [[luatex], [yes], [], [LuaTeX], [pplib libpng zziplib lua53]], [[luajittex], [yes], [], [LuaJITTeX], [pplib libpng zziplib luajit]], diff --git a/Build/source/texk/web2c/c-auto.in b/Build/source/texk/web2c/c-auto.in index 9b356d00a32..7ffd2216b29 100644 --- a/Build/source/texk/web2c/c-auto.in +++ b/Build/source/texk/web2c/c-auto.in @@ -2,7 +2,7 @@ /* w2c/c-auto.h: defines for web2c, as determined by configure. - Copyright 1994-97, 2008-2012 Karl Berry. + Copyright 1994-97, 2008-2021 Karl Berry. Copyright 1997-99, 2002, 2005 Olaf Weber. This program is free software: you can redistribute it and/or modify diff --git a/Build/source/texk/web2c/configure b/Build/source/texk/web2c/configure index 974706d41e0..185c8da41a4 100755 --- a/Build/source/texk/web2c/configure +++ b/Build/source/texk/web2c/configure @@ -822,6 +822,8 @@ PDFTEX_SYNCTEX_FALSE PDFTEX_SYNCTEX_TRUE PDFTEX_FALSE PDFTEX_TRUE +HITEX_FALSE +HITEX_TRUE ALEPH_FALSE ALEPH_TRUE EUPTEX_SYNCTEX_FALSE @@ -1045,6 +1047,7 @@ enable_uptex_synctex enable_euptex enable_euptex_synctex enable_aleph +enable_hitex enable_pdftex enable_pdftex_synctex enable_luatex @@ -1784,6 +1787,7 @@ Optional Features: --disable-euptex do not compile and install e-upTeX --disable-euptex-synctex build e-upTeX without SyncTeX support --disable-aleph do not compile and install Aleph + --enable-hitex compile and install HiTeX --disable-pdftex do not compile and install pdfTeX --disable-pdftex-synctex build pdfTeX without SyncTeX support --disable-luatex do not compile and install LuaTeX @@ -19811,6 +19815,21 @@ case $enable_aleph in #( *) : enable_aleph=yes ;; esac +# Check whether --enable-hitex was given. +if test ${enable_hitex+y} +then : + enableval=$enable_hitex; +fi +case $enable_hitex in #( + yes | no) : + ;; #( + *) : + enable_hitex=no ;; +esac + +test "x$enable_web2c:$enable_hitex" = xyes:yes && { + need_zlib=yes +} # Check whether --enable-pdftex was given. if test ${enable_pdftex+y} then : @@ -22525,6 +22544,13 @@ fi else ALEPH_TRUE='#' ALEPH_FALSE= +fi + if test "x$enable_hitex" = xyes; then + HITEX_TRUE= + HITEX_FALSE='#' +else + HITEX_TRUE='#' + HITEX_FALSE= fi if test "x$enable_pdftex" = xyes; then PDFTEX_TRUE= @@ -28375,6 +28401,10 @@ if test -z "${ALEPH_TRUE}" && test -z "${ALEPH_FALSE}"; then as_fn_error $? "conditional \"ALEPH\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 fi +if test -z "${HITEX_TRUE}" && test -z "${HITEX_FALSE}"; then + as_fn_error $? "conditional \"HITEX\" was never defined. +Usually this means the macro was only invoked conditionally." "$LINENO" 5 +fi if test -z "${PDFTEX_TRUE}" && test -z "${PDFTEX_FALSE}"; then as_fn_error $? "conditional \"PDFTEX\" was never defined. Usually this means the macro was only invoked conditionally." "$LINENO" 5 diff --git a/Build/source/texk/web2c/configure.ac b/Build/source/texk/web2c/configure.ac index f2ecd7f26ca..7b00fd4fadb 100644 --- a/Build/source/texk/web2c/configure.ac +++ b/Build/source/texk/web2c/configure.ac @@ -326,7 +326,7 @@ AC_CONFIG_HEADERS([w2c/c-auto.h:c-auto.in], AH_TOP([/* w2c/c-auto.h: defines for web2c, as determined by configure. - Copyright 1994-97, 2008-2012 Karl Berry. + Copyright 1994-97, 2008-2021 Karl Berry. Copyright 1997-99, 2002, 2005 Olaf Weber. This program is free software: you can redistribute it and/or modify diff --git a/Build/source/texk/web2c/hitexdir/am/hitex.am b/Build/source/texk/web2c/hitexdir/am/hitex.am new file mode 100644 index 00000000000..31cf845c487 --- /dev/null +++ b/Build/source/texk/web2c/hitexdir/am/hitex.am @@ -0,0 +1,100 @@ +## $Id$ +## texk/web2c/hitexdir/am/hitex.am: Makefile fragment for HiTeX +## +## Copyright 2021 Martin Ruckert +## You may freely use, modify and/or distribute this file. + +if HITEX +bin_PROGRAMS += hitex hishrink histretch +endif HITEX +EXTRA_PROGRAMS += hitex hishrink histretch + +#kb hitex_CFLAGS = $(AM_CFLAGS) \ +#kb -Wno-parentheses -Wno-maybe-uninitialized -Wno-unused-function -Wno-unused-variable +hitex_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) +hitex_LDADD = $(KPATHSEA_LIBS) $(ZLIB_LIBS) +# +histretch_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) +histretch_LDADD = $(ZLIB_LIBS) +# +hishrink_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) +hishrink_LDADD = $(ZLIB_LIBS) + +# We want debugging for the parser, and we use features not in POSIX yacc. +# I guess the result is that we assume bison? Not sure if that's ok. +AM_YFLAGS = -d -v -Wno-yacc + +# HiTeX CWEB sources +hitex_web = hitexdir/format.w hitexdir/htex.w hitexdir/hitex.w + +# Creating several files: need stamp file and two rules with identical recipes +hi_ctangle_sh = CWEBINPUTS=$(srcdir)/hitexdir AM_V_P=$(AM_V_P) $(SHELL) ./tangle-sh $@ $(CTANGLE) + +# HiTeX C/yacc/lex sources generated using ctangle. +# For each cweb (.w) source file, list the files generated. +format_c_h_l_y = basetypes.h error.h hformat.h \ + mkhformat.c \ + hput.c hput.h \ + hget.c hget.h \ + shrink.lex-in shrink.yacc-in \ + hishrink.c histretch.c +htex_c_h = htex.c htex.h +hitex_c_h = hitex.c hitex.h + +$(format_c_h_l_y): format-tangle + $(hi_ctangle_sh) format +# +format-tangle: ctangle$(EXEEXT) hitexdir/format.w tangle-sh + $(hi_ctangle_sh) format + +$(hitex_c_h): hitex-tangle + $(hi_ctangle_sh) hitex +# +hitex-tangle: ctangle$(EXEEXT) hitexdir/hitex.w tangle-sh + $(hi_ctangle_sh) hitex + +# htex.c needs hitex.h, so depend on the hitex tangle. +$(htex_c_h): htex-tangle hitex-tangle + $(hi_ctangle_sh) htex +# +htex-tangle: ctangle$(EXEEXT) hitexdir/htex.w tangle-sh hitex-tangle + $(hi_ctangle_sh) htex + +# Generating hformat.c using mkhformat. +hformat.c: mkhformat + ./mkhformat >hformat.c || { rm -f hformat.c; exit 1; } + +# Postprocessing lex and yacc files generated by ctangle. +# (SED is defined with AC_PROG_SED) +shrink-parser.y: $(srcdir)/hitexdir/yacc.sed shrink.yacc-in + $(SED) -f $(srcdir)/hitexdir/yacc.sed shrink.yacc-in >shrink-parser.y \ + || { rm -f shrink-parser.y; exit 1; } +# +shrink-lexer.l: $(srcdir)/hitexdir/lex.sed shrink.lex-in + $(SED) -f $(srcdir)/hitexdir/lex.sed shrink.lex-in >shrink-lexer.l \ + || { rm -f shrink-lexer.l; exit 1; } + +# Other web2c/*dir/am/*.am engine Makefile fragments define a bunch of +# _OBJECTS variables. This does not seem right to me. Instead, we +# correctly define the _SOURCES. +# +nodist_hitex_SOURCES = hformat.c hput.c $(htex_c_h) $(hitex_c_h) +nodist_histretch_SOURCES = hformat.c histretch.c +nodist_hishrink_SOURCES = hformat.c hishrink.c shrink-lexer.l shrink-parser.y + +# shrink needs the yacc header. It's actually the .o that should be the +# target here, seems to me, but hishrink.o is not used for the object +# name, it's hishrink-hishrink.o (because the program is also named +# hishrink), and adding that here ends up trying to link with +# hishrink-hishrink.o (I don't know), which doesn't exist. Sigh. +hishrink.c: shrink-parser.h shrink-parser.c + +EXTRA_DIST += hitexdir/ChangeLog $(hitex_web) lex.sed yacc.sed + +DISTCLEANFILES += $(nodist_hitex_SOURCES) +DISTCLEANFILES += $(nodist_hishrink_SOURCES) $(nodist_histretch_SOURCES) +DISTCLEANFILES += format-tangle htex-tangle hitex-tangle +DISTCLEANFILES += shrink-parser.* shrink-lexer.* + +## HiTeX tests +# still missing diff --git a/Build/source/texk/web2c/hitexdir/format.w b/Build/source/texk/web2c/hitexdir/format.w new file mode 100644 index 00000000000..0aa8152ccaa --- /dev/null +++ b/Build/source/texk/web2c/hitexdir/format.w @@ -0,0 +1,10509 @@ +% This file is part of HINT +% Copyright 2017-2021 Martin Ruckert +% +% 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 ../hint.sty + +%% 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 + + +\def\setrevision$#1: #2 ${\gdef\lastrevision{#2}} +\setrevision$Revision: 2485 $ +\def\setdate$#1(#2) ${\gdef\lastdate{#2}} +\setdate$Date: 2021-08-19 19:37:13 +0200 (Thu, 19 Aug 2021) $ + +\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 HINT:\par + \Largetitlefont\raggedleft The File Format\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 HINT: The File Format}\par + \leftline{\smalltitlefont Version 1.3} + \vskip 10pt plus 0.5fill + \leftline{\smalltitlefont Reflowable} + \vskip-3pt + \leftline{\smalltitlefont Output} + \vskip-3pt + \leftline{\smalltitlefont for \TeX} + \vskip 10pt plus 0.5fill + \hskip 0pt plus 2fill{\it F\"ur meine Mutter}\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{Second 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. + HINT: The File Format + Includes index. + ISBN 979-854992684-4 +} +\bigskip + +{\raggedright\advance\rightskip 3.5pc +\def\:{\discretionary{}{}{}} +Internet page {\tt http:\://hint.\:userweb.\:mwn.\:de/\:hint/\:format.html} +may contain current information about this book, downloadable software, +and news. + +\vfill +Copyright $\copyright$ 2019, 2021 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 ruckert\:@@cs.hm.edu} +\medskip +ISBN-13: 979-854992684-4\par +\medskip +First printing: August 2019\par +Second edition: August 2021\par +\medskip +Revision: \lastrevision,\quad Date: \lastdate\par +} +} +\endgroup + + +\frontmatter + + + +\plainsection{Preface} +Late in summer 2017, with my new \CEE\ based {\tt cweb} implementation +of \TeX\cite{Knuth:tex} in hand\cite{MR:webtocweb}\cite{MR:tug38}\cite{MR:web2w}, I started to write +the first prototype of the \HINT\ viewer. I basically made two copies +of \TeX: In the first copy, I replaced the |build_page| procedure by +an output routine which used more or less the printing routines +already available in \TeX. This was the beginning of the +\HINT\ file format. +In the second copy, I replaced \TeX's main loop by an input routine +that would feed the \HINT\ file more or less directly to \TeX's +|build_page| procedure. And after replacing \TeX's |ship_out| +procedure by a modified rendering routine of a dvi viewer that I had +written earlier for my experiments with \TeX's Computer Modern +fonts\cite{MR:tug37}, I had my first running \HINT\ viewer. My +sabbatical during the following Fall term gave me time for ``rapid +prototyping'' various features that I considered necessary for +reflowable \TeX\ output\cite{MR:tug39}. + +The textual output format derived from the original \TeX\ debugging +routines proved to be insufficient when I implemented a ``page up'' +button because it did not support reading the page content +``backwards''. As a consequence, I developed a compact binary file +format that could be parsed easily in both directions. The \HINT\ +short file format war born. I stopped an initial attempt at +eliminating the old textual format because it was so much nicer when +debugging. Instead, I converted the long textual format into the short +binary format as a preliminary step in the viewer. This was not a long +term solution. When opening a big file, as produced from a 1000 +pages \TeX\ file, the parsing took several seconds before the first +page would appear on screen. This delay, observed on a fast desktop +PC, is barley tolerable, and the delay one would expect on a low-cost, +low-power, mobile device seemed prohibitive. The consequence is +simple: The viewer will need an input file in the short format; and to +support debugging (or editing), separate programs are needed to +translate the short format into the long format and back again. But +for the moment, I did not bother to implement any of this but +continued with unrestricted experimentation. + +With the beginning of the Spring term 2018, I stopped further +experiments with the \HINT\ viewer and decided that I had to write +down a clean design of the \HINT\ file format. Or of both file +formats? Professors are supposed to do research, and hence I tried an +experiment: Instead of writing down a traditional language +specification, I decided to stick with the ``literate programming'' +paradigm\cite{Knuth:lp} and write the present book. It describes and implements +the \.{stretch} and \.{shrink} programs translating one file format +into the other. As a side effect, it contains the underlying language +specification. Whether this experiment is a success as a language +specification remains to be seen, and you should see for yourself. But +the only important measure for the value of a scientific experiment is +how much you can learn form it---and I learned a lot. + +The whole project turned out to be much more difficult than I had +expected. Early on, I decided that I would use a recursive descent +parser for the short format and an LR($k$) parser for the long +format. Of course, I would use {\tt lex}/{\tt flex} and {\tt yacc}/{\tt bison} +to generate the LR($k$) parser, and so I had to extend the {\tt +cweb} tools\cite{Knuth:cweb} to support the corresponding source files. + +About in mid May, after writing down about 100 pages, the first +problems emerged that could not be resolved with my current +approach. I had started to describe font definitions containing +definitions of the interword glue and the default hyphen, and the +declarative style of my exposition started to conflict with the +sequential demands of writing an output file. So it was time for a +first complete redesign. Two more passes over the whole book were +necessary to find the concepts and the structure that would allow me +to go forward and complete the book as you see it now. + +While rewriting was on its way, many ``nice ideas'' were pruned from +the book. For example, the initial idea of optimizing the \HINT\ file +while translating it was first reduced to just gathering statistics +and then disappeared completely. The added code and complexity was +just too distracting. + +What you see before you is still a snapshot of the \HINT\ file format +because its development is still under way. We will know what +features are needed for a reflowable \TeX\ file format only after many +people have started using the format. To use the format, the end-user +will need implementations, and the implementer will need a language +specification. The present book is the first step in an attempt to +solve this ``chicken or egg'' dilemma. + + +\vskip 1cm +\noindent {\it M\"unchen\hfil\break +August 20, 2019 \hfill Martin Ruckert} + + +\tableofcontent +%\thefigindex + + +\mainmatter + +\section{Introduction}\label{intro} +This book defines a file format for reflowable text. +Actually it describes two file formats: a long format +that optimizes readability for human beings, and +a short format that optimizes readability for machines +and the use of storage space. Both formats use the concept of nodes and lists of +nodes to describe the file content. Programs that process these nodes +will likely want to convert the compressed binary representation of a +node---the short format---or the lengthy textual representation of a +node---the long format---into a convenient internal representation. +So most of what follows is just a description of these nodes: their short format, +their long format and sometimes their internal representation. +Where as the description of the long and short external format is part +of the file specification, the description of the internal representation +is just informational. Different internal representations can be chosen +based on the individual needs of the program. + +While defining the format, I illustrate the processing of long and short format +files by implementing two utilities: \.{shrink} and \.{stretch}. +\.{shrink} converts the long format into the short format and \.{stretch} +goes the other way. + +There is also a prototype viewer for this +file format and a special version of \TeX\cite{DK:texbook} to produce output +in this format. Both are not described here; a survey describing +them can be found in \cite{MR:tug39}. + +\subsection{Glyphs} +Let's start with a simple and very common kind of node: a node describing +a character. +Because we describe a format that is used to display text, +we are not so much interested in the +character itself but we are interested in the specific glyph\index{glyph}. +In typography, a glyph is a unique mark to be placed on the page representing +a character. For example the glyph representing the character `a' can have +many forms among them `{\it a\/}', `{\bf a}', or `{\tenss a}'. +Such glyphs come in collections, called fonts, representing every character +of the alphabet in a consistent way. + +The long format of a node describing the glyph `a' + might look like this:`` \.{}''. +Here ``\.{97}'' is the character code which +happens to be the ASCII code of the letter `a' and ``{\tt *1}'' is a font reference +that stands for ``Computer Modern Roman 10pt''. +Reference numbers, as you can see, +start with an asterisk reminiscent of references in the \CEE\ programming language. +The Astrix enables us to distinguish between ordinary numbers like ``\.{1}'' and references like ``{\tt *1}''. + +To make this node more readable, we will see in section~\secref{chars} that it is also +possible to write `` \.{}''. +The latter form uses a comment ``\.{(cmr10)}'', enclosed in parentheses, to +give an indication of what kind of font happens to be font 1, and it uses ``\.{'a'}'', +the character enclosed in single quotes to denote the ASCII code of `a'. +But let's keep things simple for now and stick with the decimal notation of the character code. + +The rest is common for all nodes: a keyword, here ``\.{glyph}'', and a pair of pointed brackets ``\.{<}\dots\.{>}''. + +Internally, we represent a glyph by the font number +and the character number or character code. +To store the internal representation of a glyph node, +we define an appropriate structure type, named after the node with a trailing {\dots\bf\_t}. +@= +typedef struct {@+ uint32_t c;@+ uint8_t f; @+} glyph_t; +@ + +Let us now look at the program \.{shrink} and see how it will convert the long format description +to the internal representation of the glyph and finally to a short format description. + + +\subsection{Scanning the Long Format} +First, \.{shrink} reads the input file and extracts a sequence of tokens. This is called ``scanning''\index{scanning}. +We generate the procedure to do the scanning using the program \.{flex}\cite{JL:flexbison}\index{flex+{\tt flex}} which is the +GNU version of the common UNIX tool \.{lex}\cite{JL:lexyacc}\index{lex+{\tt lex}}. + +The input to \.{flex} is a list of pattern/\kern -1pt action rules where the pattern is a regular +expression and the action is a piece of \CEE\ code. +Most of the time, the \CEE\ code is very simple: it just returns the right token\index{token} number +to the parser which we consider shortly. + +The code that defines the tokens will be marked with a line ending in ``\redsymbol''. +This symbol\index{symbol} stands for ``{\it Reading the long format\/}''. +These code sequences define the syntactical elements of the long format and at the same time +implement the reading process. All sections where that happens are preceded by a similar heading +and for reference they are conveniently listed together starting on page~\pageref{codeindex}. + +\codesection{\redsymbol}{Reading the Long Format}\redindex{1}{2}{Glyphs} +@s START symbol +@s END symbol +@s GLYPH symbol +@s UNSIGNED symbol +@s REFERENCE symbol + +@= +%token START "<" +%token END ">" +%token GLYPH "glyph" +%token UNSIGNED +%token REFERENCE +@ +You might notice that a small caps font is used for |START|, |END| or |GLYPH|. +These are ``terminal symbols'' or ``tokens''. +Next are the scanning rules which define the connection between tokens and their +textual representation. + +@= +::@="<"@> :< SCAN_START; return START; >: +::@=">"@> :< SCAN_END; return END; >: +::@=glyph@> :< return GLYPH; >: +::@=0|[1-9][0-9]*@> :< SCAN_UDEC(yytext); return UNSIGNED; >: +::@=\*(0|[1-9][0-9]*)@> :< SCAN_UDEC(yytext+1); return REFERENCE; >: +::@=[[:space:]]@> :< ; >: +::@=\([^()\n]*[)\n]@> :< ; >: +@ + +As we will see later, the macros starting with |SCAN_|\dots\ are scanning macros. +Here |SCAN_UDEC| is a macro that converts the decimal representation +that did match the given pattern to an unsigned integer value; it is explained in +section~\secref{integers}. +The macros |SCAN_START| and |SCAN_END| are explained in section~\secref{text}. + + +The action ``{\tt ;}'' is a ``do nothing'' action; here it causes spaces or comments\index{comment} +to be ignored. Comments start with an opening parenthesis and are terminated by a +closing parenthesis or the end of line character. +The pattern ``\.{[\^()\\n]}'' is a negated +character class that matches all characters except parentheses and the newline +character. These are not allowed inside comments. For detailed information about +the patterns used in a \.{flex} program, see the \.{flex} user manual\cite{JL:flexbison}. + +\subsection{Parsing the Long Format} +\label{parse_glyph} +Next, the tokens produced by the scanner are assembled into larger entities. +This is called ``parsing''\index{parsing}. +We generate the procedure to do the parsing using the program \.{bison}\cite{JL:flexbison}\index{bison+{\tt bison}} which is +the GNU version of the common UNIX tool \.{yacc}\cite{JL:lexyacc}\index{yacc+{\tt yacc}}. + +The input to \.{bison} is a list of parsing rules, called a ``grammar''\index{grammar}. +The rules describe how to build larger entities from smaller entities. +For a simple glyph node like `` \.{}'', we need just these rules: +\codesection{\redsymbol}{Reading the Long Format}%\redindex{1}{2}{Glyphs} +@s content_node symbol +@s node symbol +@s glyph symbol +@s glyph_t int +@s start symbol +@= +%type start +%type glyph +@ + +@=@/ +glyph: UNSIGNED REFERENCE @/{ $$.c=$1; REF(font_kind,$2); $$.f=$2; }; +content_node: start GLYPH glyph END { hput_tags($1,hput_glyph(&($3))); }; +start: START {HPUTNODE; $$=(uint32_t)(hpos++-hstart);} +@ + +You might notice that a slanted font is used for |glyph|, |content_node|, or |start|. +These are ``nonterminal symbols' and occur on the left hand side of a rule. On the +right hand side of a rule you find nonterminal symbols, as well as terminal\index{terminal symbol} symbols +and \CEE\ code enclosed in braces. + +Within the \CEE\ code, the expressions |$1| and |$2| refer to the variables on the parse stack +that are associated with the first and second symbol on the right hand side of the rule. +In the case of our glyph node, these will be the values 97 and 1, respectively, as produced +by the macro |SCAN_UDEC|. +|$$| refers to the variable associated with the left hand side of the rule. +These variables contain the internal representation of the object in question. +The type of the variable is specified by a mandatory {\bf token} or optional {\bf type} clause +when we define the symbol. +In the above {\bf type} clause for |start| and |glyph| , the identifiers |u| and |c| refer to +the |union| declaration of the parser (see page~\pageref{union}) +where we find |uint32_t u| and |glyph_t c|. The macro |REF| tests a reference number for +its valid range. + + +Reading a node is usually split into the following sequence of steps: +\itemize +\item Reading the node specification, here a |glyph| + consisting of an |UNSIGNED| value and a |REFERENCE| value. +\item Creating the internal representation in the variable |$$| + based on the values of |$1|, |$2|, \dots\ Here the character + code field |c| is initialized using the |UNSIGNED| value + stored in |$1| and the font field |f| is initialized using + |$2| after checking the reference number for the proper range. +\item A |content_node| rule explaining that |start| is followed by |GLYPH|, + the keyword that directs the parser to |glyph|, the + node specification, and a final |END|. +\item Parsing |start|, which is defined as the token |START| will assign + to the corresponding variable |p| on the parse stack the current + position |hpos| in the output and increments that position + to make room for the start byte, which we will discuss shortly. +\item At the end of the |content_node| rule, the \.{shrink} program calls + a {\it hput\_\dots\/} function, here |hput_glyph|, to write the short + format of the node as given by its internal representation to the output + and return the correct tag value. +\item Finally the |hput_tags| function will add the tag as a start byte and end byte + to the output stream. +\enditemize + +Now let's see how writing the short format works in detail. + + +\subsection{Writing the Short Format} +A content node in short form begins with a start\index{start byte} byte. It tells us what kind of node it is. +To describe the content of a short \HINT\ file, 32 different kinds\index{kind} of nodes are defined. +Hence the kind of a node can be stored in 5 bits and the remaining bits of the start byte +can be used to contain a 3 bit ``info''\index{info} value. + +We define an enumeration type to give symbolic names to the kind-values. +The exact numerical values are of no specific importance; +we will see in section~\secref{text}, however, that the assignment chosen below, +has certain advantages. + +Because the usage of kind-values in content nodes is +slightly different from the usage in definition nodes, we define alternative names for some kind-values. +To display readable names instead of numerical values when debugging, +we define two arrays of strings as well. Keeping the definitions consistent +is achieved by creating all definitions from the same list +of identifiers using different definitions of the macro |DEF_KIND|. + +@= +#define DEF_KIND(C,D,N) @[C##_kind=N@] +typedef enum {@+@@+,@+ @ @+} kind_t; +#undef DEF_KIND +@ + +@= + +#define DEF_KIND(C,D,N) @[#C@] +const char *content_name[32]=@+{@+@@;@+}@+; +#undef DEF_KIND@# +#define DEF_KIND(C,D,N) @[#D@] +const char *definition_name[0x20]=@+{@+@@;@+}@+; +#undef DEF_KIND +@ + +@= +printf("const char *content_name[32]={"); +for (k=0; k<= 31;k++) +{ printf("\"%s\"",content_name[k]); + if (k<31) printf(", "); +} +printf("};\n\n"); +printf("const char *definition_name[32]={"); +for (k=0; k<= 31;k++) +{ printf("\"%s\"",definition_name[k]); + if (k<31) printf(", "); +} +printf("};\n\n"); +@ + + + + + + +\goodbreak +\index{glyph kind+\\{glyph\_kind}} +\index{font kind+\\{font\_kind}} +\index{penalty kind+\\{penalty\_kind}} +\index{int kind+\\{int\_kind}} +\index{kern kind+\\{kern\_kind}} +\index{xdimen kind+\\{xdimen\_kind}} +\index{ligature kind+\\{ligature\_kind}} +\index{disc kind+\\{disc\_kind}} +\index{glue kind+\\{glue\_kind}} +\index{language kind+\\{language\_kind}} +\index{rule kind+\\{rule\_kind}} +\index{image kind+\\{image\_kind}} +\index{baseline kind+\\{baseline\_kind}} +\index{dimen kind+\\{dimen\_kind}} +\index{hbox kind+\\{hbox\_kind}} +\index{vbox kind+\\{vbox\_kind}} +\index{par kind+\\{par\_kind}} +\index{math kind+\\{math\_kind}} +\index{table kind+\\{table\_kind}} +\index{item kind+\\{item\_kind}} +\index{hset kind+\\{hset\_kind}} +\index{vset kind+\\{vset\_kind}} +\index{hpack kind+\\{hpack\_kind}} +\index{vpack kind+\\{vpack\_kind}} +\index{stream kind+\\{stream\_kind}} +\index{page kind+\\{page\_kind}} +\index{range kind+\\{range\_kind}} +\index{adjust kind+\\{adjust\_kind}} +\index{param kind+\\{param\_kind}} +\index{text kind+\\{text\_kind}} +\index{list kind+\\{list\_kind}} +\label{kinddef} +@= +DEF_KIND(t@&ext,t@&ext,0),@/ +DEF_KIND(l@&ist,l@&ist,1),@/ +DEF_KIND(p@&aram,p@&aram,2),@/ +DEF_KIND(x@&dimen,x@&dimen,3),@/ +DEF_KIND(a@&djust,a@&djust,4),@/ +DEF_KIND(g@&lyph, f@&ont,5),@/ +DEF_KIND(k@&ern,d@&imen,6),@/ +DEF_KIND(g@&lue,g@&lue,7),@/ +DEF_KIND(l@&igature,l@&igature,8),@/ +DEF_KIND(d@&isc,d@&isc,9),@/ +DEF_KIND(l@&anguage,l@&anguage,10),@/ +DEF_KIND(r@&ule,r@&ule,11),@/ +DEF_KIND(i@&mage,i@&mage,12),@/ +DEF_KIND(l@&eaders,l@&eaders,13),@/ +DEF_KIND(b@&aseline,b@&aseline,14),@/ +DEF_KIND(h@&b@&ox,h@&b@&ox,15),@/ +DEF_KIND(v@&b@&ox,v@&b@&ox,16),@/ +DEF_KIND(p@&ar,p@&ar,17),@/ +DEF_KIND(m@&ath,m@&ath,18),@/ +DEF_KIND(t@&able,t@&able,19),@/ +DEF_KIND(i@&tem,i@&tem,20),@/ +DEF_KIND(h@&set,h@&set,21),@/ +DEF_KIND(v@&set,v@&set,22),@/ +DEF_KIND(h@&pack,h@&pack,23),@/ +DEF_KIND(v@&pack,v@&pack,24),@/ +DEF_KIND(s@&tream,s@&tream,25),@/ +DEF_KIND(p@&age,p@&age,26),@/ +DEF_KIND(r@&ange,r@&ange,27),@/ +DEF_KIND(l@&ink,l@&abel,28),@/ +DEF_KIND(u@&ndefined2,u@&ndefined2,29),@/ +DEF_KIND(u@&ndefined3,u@&ndefined3,30),@/ +DEF_KIND(p@&enalty, i@&nt,31) +@t@> +@ + +For a few kind-values we have +alternative names; we will use them +to express different intentions when using them. +@= +font_kind=glyph_kind,int_kind=penalty_kind, dimen_kind=kern_kind, label_kind=link_kind, outline_kind=link_kind@/@t{}@> +@ + +The info\index{info value} values can be used to represent numbers in the range 0 to 7; for an example +see the |hput_glyph| function later in this section. +Mostly, however, the individual bits are used as flags indicating the presence +or absence of immediate parameter values. If the info bit is set, it +means the corresponding parameter is present as an immediate value; if it +is zero, it means that there is no immediate parameter value present, and +the node specification will reveal what value to use instead. +In some cases there is a common default value that can be used, in other +cases a one byte reference number is used to select a predefined value. + +To make the binary +representation of the info bits more readable, we define an +enumeration type. + +\index{b000+\\{b000}} +\index{b001+\\{b001}} +\index{b010+\\{b010}} +\index{b011+\\{b011}} +\index{b100+\\{b100}} +\index{b101+\\{b101}} +\index{b110+\\{b110}} +\index{b111+\\{b111}} +@= +typedef enum {@+ b000=0,b001=1,b010=2,b011=3,b100=4,b101=5,b110=6,b111=7@+ } info_t; +@ + + +After the start byte follows the node content and it is the purpose of +the start byte to reveal the exact syntax and semantics of the node +content. Because we want to be able to read the short form of a \HINT\ +file in forward direction and in backward direction, the start byte is +duplicated after the content as an end\index{end byte} byte. + + +We store a kind and an info value in one byte and call this a tag. +The following macros are used to assemble and disassemble tags:\index{TAG+\.{TAG}} +@= +#define @[KIND(T)@] (((T)>>3)&0x1F) +#define @[NAME(T)@] @[content_name[KIND(T)]@] +#define @[INFO(T)@] ((T)&0x7) +#define @[TAG(K,I)@] (((K)<<3)|(I)) +@ + +Writing a short format \HINT\ file is implemented by a collection of {\it hput\_\kern 1pt\dots\/} functions; +they follow most of the time the same schema: +\itemize +\item First, we define a variable for |info|. +\item Then follows the main part of the function body, where we +decide on the output format, do the actual output and set the |info| value accordingly. +\item We combine the info value with the kind-value and return the correct tag. +\item The tag value will be passed to |hput_tags| which generates +debugging information, if requested, and stores the tag before and after the node content. +\enditemize + + +After these preparations, we turn our attention again to the |hput_glyph| function. +The font number in a glyph node is between 0 and 255 and fits nicely in one byte, +but the character code is more difficult: we want to store the most common character +codes as a single byte and less frequent codes with two, three, or even four byte. +Naturally, we use the |info| bits to store the number of bytes needed for the character code. + +\codesection{\putsymbol}{Writing the Short Format}\putindex{1}{2}{Glyphs} +@= +static uint8_t hput_n(uint32_t n) +{@+ if (n<=0xFF) @+ + {@+HPUT8(n);@+ return 1;@+} + else if (n<=0xFFFF) @+ + {@+HPUT16(n);@+ return 2;@+} + else if (n<=0xFFFFFF)@+ + {@+HPUT24(n);@+ return 3;@+} + else @+ + {@+HPUT32(n);@+ return 4;@+} +} + +uint8_t hput_glyph(glyph_t *g) +{ info_t info; + info = hput_n(g->c); + HPUT8(g->f);@/ + return TAG(glyph_kind,info); +} +@ +The |hput_tags| function is called after the node content has been written to the +stream. It gets a the position of the start byte and the tag. With this information +it writes the start byte at the given position and the end byte at the current stream position. +@= +void hput_tags(uint32_t pos, uint8_t tag) +{ DBGTAG(tag,hstart+pos);DBGTAG(tag,hpos); + HPUTX(1); *(hstart+pos)=*(hpos++)=tag; @+ +} +@ + + + +The variables |hpos| and |hstart|, the macros |HPUT8|, |HPUT16|, +|HPUT24|, |HPUT32|, and |HPUTX| are all defined in +section~\secref{HPUT}; they put 8, 16, 24, or 32 bits into the output +stream and check for sufficient space in the output buffer. The macro +|DBGTAG| writes debugging output; its definition is found in +section~\secref{error_section}. + +Now that we have seen the general outline of the \.{shrink} program, +starting with a long format file and ending with a short format file, +we will look at the program \.{stretch} that reverses this +transformation. + + +\subsection{Parsing the Short Format} +The inverse of writing the short format with a {\it hput\_\kern 1pt\dots\/} function +is reading the short format with a {\it hget\_\kern 1pt\dots\/} function. + +The schema of {\it hget\_\kern 1pt\dots\/} functions reverse the schema of {\it hput\_\kern 1pt\dots\/} functions. +Here is the code for the initial and final part of a get function: + +@= +uint8_t a,z; /* the start and the end byte*/ +uint32_t node_pos=hpos-hstart; +if (hpos>=hend) QUIT("Attempt to read a start byte at the end of the section"); +HGETTAG(a);@/@t{}@> +@ + +@= +HGETTAG(z);@+ +if (a!=z) + QUIT(@["Tag mismatch [%s,%d]!=[%s,%d] at 0x%x to " SIZE_F "\n"@],@| + NAME(a),INFO(a),NAME(z),INFO(z),@|node_pos, hpos-hstart-1); +@ + + +The central routine to parse\index{parsing} the content section of a short format +file is the function |hget_content_node| which calls |hget_content| to +do most of the processing. + +|hget_content_node| will read a content node in short format and write +it out in long format: It reads the start\index{start byte} byte |a|, writes the |START| +token using the function |hwrite_start|, and based on |KIND(a)|, it +writes the node's keyword found in the |content_name| array. Then it +calls |hget_content| to read the node's content and write it out. +Finally it reads the end\index{end byte} byte, checks it against the start byte, and +finishes up the content node by writing the |END| token using the +|hwrite_end| function. The function returns the tag byte so that +the calling function might check that the content node meets its requirements. + +|hget_content| uses the start byte |a|, passed as a parameter, to +branch directly to the reading routine for the given combination of +kind and info value. The reading routine will read the data and store +its internal representation in a variable. All that the \.{stretch} +program needs to do with this internal representation is writing it in +the long format. As we will see, the call to the proper +{\it hwrite\_\kern 1pt\dots} function is included as final part of the the +reading routine (avoiding another switch statement). + + +\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{2}{Content Nodes} +@= +void hget_content(uint8_t a); +uint8_t hget_content_node(void) +{ @@;@+ hwrite_start(); + hwritef("%s",content_name[KIND(a)]); + hget_content(a);@/ + @@; hwrite_end(); + return a; +} + +void hget_content(uint8_t a) +{@+ + switch (a)@/ + {@+ + @@;@t\1@>@/ + default: + TAGERR(a); + break;@t\2@>@/ + } +} +@ + +We implement the code to read a glyph node in two stages. +First we define a general reading macro |HGET_GLYPH(I,G)| that reads a glyph node with info value |I| into +a |glyph_t| variable |G|; then we insert this macro +in the above switch statement for all cases where it applies. +Knowing the function |hput_glyph|, the macro |HGET_GLYPH| should not be a surprise. +It reverses |hput_glyph|, storing the glyph node in its internal representation. +After that, the \.{stretch} program calls |hwrite_glyph| to produce the glyph +node in long format. + +\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{2}{Glyphs} +@= +#define @[HGET_N(I,X)@] \ + if ((I)==1) (X)=HGET8;\ + else if ((I)==2) HGET16(X);\ + else if ((I)==3) HGET24(X);\ + else if ((I)==4) HGET32(X); + +#define @[HGET_GLYPH(I,G)@] \ + HGET_N(I,(G).c); (G).f=HGET8; @+REF_RNG(font_kind,(G).f);@/\ + hwrite_glyph(&(G));\ +@ + +Note that we allow a glyph to reference a font even before that font is defined. +This is necessary because fonts usually contain definitions---for example +the fonts hyphen character---that reference this or other fonts. + + +@= +@t\1\kern1em@>case TAG(glyph_kind,1): @+{@+glyph_t g;@+ HGET_GLYPH(1,g);@+}@+break; +case TAG(glyph_kind,2): @+{@+glyph_t g;@+ HGET_GLYPH(2,g);@+}@+break; +case TAG(glyph_kind,3): @+{@+glyph_t g;@+ HGET_GLYPH(3,g);@+}@+break; +case TAG(glyph_kind,4): @+{@+glyph_t g;@+ HGET_GLYPH(4,g);@+}@+break; +@ + +If this two stage method seems strange to you, consider what the \CEE\ compiler will +do with it. It will expand the |HGET_GLYPH| macro four times inside the switch +statement. The macro is, however, expanded with a constant |I| value, so the expansion +of the |if| statement in |HGET_GLYPH(1,g)|, for example, +will become ``|if (1==1)| \dots\ |else if (1==2)| \dots'' +and the compiler will have no difficulties eliminating the constant tests and +the dead branches altogether. This is the most effective use of the switch statement: +a single jump takes you to a specialized code to handle just the given combination +of kind and info value. + +Last not least, we implement the function |hwrite_glyph| to write a +glyph node in long form---that is: in a form that is as readable as possible. + +\subsection{Writing the Long Format} + +The |hwrite_glyph| function inverts the scanning and parsing process we have described +at the very beginning of this chapter. +To implement the |hwrite_glyph| function, we use the function |hwrite_charcode| +to write the character code. +Besides writing the character code as a decimal number, this function can handle also other +representations of character codes as fully explained in section~\secref{chars}. +We split off the writing of the opening and the closing pointed bracket, because +we will need this function very often and because it will keep track of the |nesting| +of nodes and indent them accordingly. The |hwrite_range| and |hwrite_label| functions +used in |hwrite_end| are discussed in section~\secref{range} and~\secref{hwritelabel}. + +\codesection{\wrtsymbol}{Writing the Long Format}\wrtindex{1}{2}{Glyphs} +@= +int nesting=0; +void hwrite_nesting(void) +{ int i; + hwritec('\n'); + for (i=0;i'); + if (section_no==2) + { if (nesting==0) hwrite_range(); + hwrite_label(); + } +} + +void hwrite_comment(char *str) +{ char c; + if (str==NULL) return; + hwritef(" ("); + while ((c=*str++)!=0) + if (c=='(' || c==')') hwritec('_'); + else if (c=='\n') hwritef("\n("); + else hwritec(c); + hwritec(')'); +} + +void hwrite_charcode(uint32_t c); +void hwrite_ref(int n); + +void hwrite_glyph(glyph_t *g) +{ char *n=hfont_name[g->f]; + hwrite_charcode(g->c); + hwrite_ref(g->f); + if (n!=NULL) hwrite_comment(n); +} +@ + +The two primitive operations to write the long format file are defined +as macros: + +@= +#define @[hwritec(c)@] @[putc(c,hout)@] +#define @[hwritef(...)@] @[fprintf(hout,__VA_ARGS__)@] +@ + + +Now that we have completed the round trip of shrinking and stretching +glyph nodes, we continue the description of the \HINT\ file formats +in a more systematic way. + + +\section{Data Types}\hascode +\subsection{Integers} +\label{integers} +We have already seen the pattern/\kern -1pt action rule for unsigned decimal\index{decimal number} numbers. It remains +to define the macro |SCAN_UDEC| which converts a string containing an unsigned\index{unsigned} decimal +number into an unsigned integer\index{integer}. +We use the \CEE\ library function | strtoul|: + +\readcode +@= +#define @[SCAN_UDEC(S)@] @[yylval.u=strtoul(S,NULL,10)@] +@ +Unsigned integers can be given in hexadecimal\index{hexadecimal} notation as well. +@= +::@=HEX@> :< @=[0-9A-F]@> >: +@ + +@= +::@=0x{HEX}+@> :< SCAN_HEX(yytext+2); return UNSIGNED; >: +@ + +Note that the pattern above allows only upper case letters in the +hexadecimal notation for integers. + +@= +#define @[SCAN_HEX(S)@] @[yylval.u=strtoul(S,NULL,16)@] +@ + +Last not least, we add rules for signed\index{signed integer} integers. +@s SIGNED symbol +@s number symbol +@s integer symbol + +@= +%token SIGNED +%type integer +@ + +@= +::@=[+-](0|[1-9][0-9]*)@> :< SCAN_DEC(yytext); return SIGNED; >: +@ + +@= +#define @[SCAN_DEC(S)@] @[yylval.i=strtol(S,NULL,10)@] +@ + +@= +integer: SIGNED @+| UNSIGNED { RNG("number",$1,0,INT32_MAX);}; +@ + +To preserve the ``signedness'' of an integer also for positive signed integers +in the long format, we implement the function |hwrite_signed|. + +\writecode +@= +void hwrite_signed(int32_t i) +{ if (i<0) hwritef(" -%d",-i); + else hwritef(" +%d",+i); +} +@ + +Reading and writing integers in the short format is done directly with the {\tt HPUT} and {\tt HGET} +macros. + + +\subsection{Strings} +\label{strings} +Strings\index{string} are needed in the definition part of a \HINT\ +file to specify names of objects, and in the long file format, we also use them for file\index{file name} names. +In the long format, strings are sequences of characters delimited by single quote\index{single quote} characters; +for example: ``\.{'Hello'}'' or ``\.{'cmr10-600dpi.tfm'}''; in the short format, strings are +byte sequences terminated by a zero byte. +Because file names are system dependent, we no not allow arbitrary characters in strings +but only printable ASCII codes which we can reasonably expect to be available on most operating systems. +If your file names in a long format \HINT\ file are supposed to be portable, +you should probably be even more restrictive. For example you should avoid characters like +``\.{\\}'' or ``\.{/}'' which are used in different ways for directories. + +The internal representation of a string is a simple zero terminated \CEE\ string. +When scanning a string, we copy it to the |str_buffer| keeping track +of its length in |str_length|. When done, +we make a copy for permanent storage and return the pointer to the parser. +To operate on the |str_buffer|, we define a few macros. +The constant |MAX_STR| determines the maximum size of a string (including the zero byte) to be $2^{10}$ byte. +This restriction is part of the \HINT\ file format specification. + +@= +#define MAX_STR (1<<10) /* $2^{10}$ Byte or 1kByte */ +static char str_buffer[MAX_STR]; +static int str_length; +#define STR_START @[(str_length=0)@] +#define @[STR_PUT(C)@] @[(str_buffer[str_length++]=(C))@] +#define @[STR_ADD(C)@] @[STR_PUT(C);RNG("String length",str_length,0,MAX_STR-1)@] +#define STR_END @[str_buffer[str_length]=0@] +#define SCAN_STR @[yylval.s=str_buffer@] +@ + + +To scan a string, we switch the scanner to |STR| mode when we find a quote character, +then we scan bytes in the range |0x20| to |0x7E|, which is the range of printable ASCII +characters, until we find the closing single\index{single quote} quote. +Quote characters inside the string are written as two consecutive single quote characters. + +\readcode +@s STRING symbol +@s STR symbol +@s INITIAL symbol + +@= +%x STR +@ + +@= +%token STRING +@ + +@= +::@='@> :< STR_START; BEGIN(STR); >: +{ +::@='@> :< STR_END; SCAN_STR; BEGIN(INITIAL); return STRING; >: +::@=''@> :< STR_ADD('\''); >: +::@=[\x20-\x7E]@> :< STR_ADD(yytext[0]); >: +::@=.@> :< RNG("String character",yytext[0],0x20,0x7E); >: +::@=\n@> :< QUIT("Unterminated String in line %d",yylineno); >: +} + + +@ +The function |hwrite_string| reverses this process; it must take care of the quote symbols. +\writecode +@= +void hwrite_string(char *str) +{@+hwritec(' '); + if (str==NULL) hwritef("''"); + else@/ + { hwritec('\''); + while (*str!=0)@/ + { @+if (*str=='\'') hwritec('\''); + hwritec(*str++); + } + hwritec('\''); + } +} + + +@ +In the short format, a string is just a byte sequence terminated by a zero byte. +This makes the function |hput_string|, to write a string, and the macro |HGET_STRING|, +to read a string in short format, very simple. Note that after writing an unbounded +string to the output buffer, the macro |HPUTNODE| will make sure that there is enough +space left to write the remainder of the node. + +\putcode +@= +void hput_string(char *str) +{ char *s=str; + if (s!=NULL) + { do { + HPUTX(1); + HPUT8(*s); + } while (*s++!=0); + HPUTNODE; + } + else HPUT8(0); +} +@ + +\getcode +@= +#define @[HGET_STRING(S)@] @[S=(char*)hpos;\ + while(hpos= +glyph: CHARCODE REFERENCE @|{ $$.c=$1; REF(font_kind,$2); $$.f=$2; }; +@ + + + +If the character codes are small, we can represent them using +ASCII character codes. We do not offer a special notation for very small +character codes that map to the non-printable ASCII control codes; for them, the decimal +or hexadecimal notation will suffice. +For larger character codes, we use the multibyte encoding scheme known from UTF8\index{UTF8} as +follows. Given a character code~|c|: + +\itemize +\item +Values in the range |0x00| to |0x7f| are encoded as a single byte with a leading bit of 0. + +@= +::@=UTF8_1@> :< @=[\x00-\x7F]@> >: +@ +@= +#define @[SCAN_UTF8_1(S)@] @[yylval.u=((S)[0]&0x7F)@] +@ + + +\item +Values in the range |0x80| to |0x7ff| are encoded in two byte with the first byte +having three high bits |110|, indicating a two byte sequence, and the lower five bits equal +to the five high bits of |c|. It is followed by a continuation byte having two high bits |10| +and the lower six bits +equal to the lower six bits of |c|. + +@= +::@=UTF8_2@> :< @=[\xC0-\xDF][\x80-\xBF]@> >: +@ + +@= +#define @[SCAN_UTF8_2(S)@] @[yylval.u=(((S)[0]&0x1F)<<6)+((S)[1]&0x3F)@] +@ + +\item +Values in the range |0x800| to |0xFFFF| are encoded in three byte with the first byte +having the high bits |1110| indicating a three byte sequence followed by two continuation bytes. + +@= +::@=UTF8_3@> :< @=[\xE0-\xEF][\x80-\xBF][\x80-\xBF]@> >: +@ + +@= +#define @[SCAN_UTF8_3(S)@] @[yylval.u=(((S)[0]&0x0F)<<12)+(((S)[1]&0x3F)<<6)+((S)[2]&0x3F)@] +@ + +\item +Values in the range |0x1000| to |0x1FFFFF| are encoded in four byte with the first byte +having the high bits |11110| indicating a four byte sequence followed by three continuation bytes. + +@= +::@=UTF8_4@> :< @=[\xF0-\xF7][\x80-\xBF][\x80-\xBF][\x80-\xBF]@> >: +@ + +@= +#define @[SCAN_UTF8_4(S)@] @[yylval.u=(((S)[0]&0x03)<<18)+(((S)[1]&0x3F)<<12)+@|(((S)[2]&0x3F)<<6)+((S)[3]&0x3F)@] +@ + +\enditemize + +In the long format file, we enclose a character code in single\index{single quote} quotes, just as we do for strings. +This is convenient but it has the downside that we must exercise special care when giving the +scanning rules in order +not to confuse character codes with strings. Further we must convert character codes back into strings +in the rare case where the parser expects a string and gets a character code because the string +was only a single character long. + +Let's start with the first problem: +The scanner might confuse a string\index{string} and a character code if the first or second +character of the string is a quote character which is written as two consecutive quotes. +For example \.{'a''b'} is a string with three characters, ``\.{a}'', +``\.{'}'', and ``\.{b}''. Two character codes would need a space to separate +them like this: \.{'a' 'b'}. + + +@s CHARCODE symbol +@= +%token CHARCODE +@ + +@= +::@='''@> :< STR_START; STR_PUT('\''); BEGIN(STR); >: +::@=''''@> :< SCAN_UTF8_1(yytext+1); return CHARCODE; >: +::@='[\x20-\x7E]''@> :< STR_START; STR_PUT(yytext[1]); STR_PUT('\''); BEGIN(STR); >: +::@='''''@> :< STR_START; STR_PUT('\''); STR_PUT('\''); BEGIN(STR); >: +::@='{UTF8_1}'@> :< SCAN_UTF8_1(yytext+1); return CHARCODE; >: +::@='{UTF8_2}'@> :< SCAN_UTF8_2(yytext+1); return CHARCODE; >: +::@='{UTF8_3}'@> :< SCAN_UTF8_3(yytext+1); return CHARCODE; >: +::@='{UTF8_4}'@> :< SCAN_UTF8_4(yytext+1); return CHARCODE; >: +@ + +If needed, the parser can convert character codes back to single character strings. + +@s string symbol + +@= +%type string +@ + +@= +string: STRING @+ | CHARCODE { static char s[2]; + RNG("String element",$1,0x20,0x7E); + s[0]=$1; s[1]=0; $$=s;}; +@ + + +The function |hwrite_charcode| will write a character code. While ASCII codes are handled directly, +larger character codes are passed to the function |hwrite_utf8|. +It returns the number of characters written. + +\writecode +@= +int hwrite_utf8(uint32_t c) +{@+ if (c<0x80) + { hwritec(c); @+return 1;@+ } + else if (c<0x800)@/ + { hwritec(0xC0|(c>>6));@+ hwritec(0x80|(c&0x3F));@+ return 2;@+} + else if (c<0x10000)@/ + { hwritec(0xE0|(c>>12)); hwritec(0x80|((c>>6)&0x3F));@+ hwritec(0x80|(c&0x3F)); return 3; } + else if (c<0x200000)@/ + { hwritec(0xF0|(c>>18));@+ hwritec(0x80|((c>>12)&0x3F)); + hwritec(0x80|((c>>6)&0x3F));@+ hwritec(0x80|(c&0x3F)); return 4;} + else + RNG("character code",c,0,0x1FFFFF); + return 0; +} + +void hwrite_charcode(uint32_t c) +{ @+if (c < 0x20) + { if (option_hex) hwritef(" 0x%02X",c); /* non printable ASCII */ + else hwritef(" %u",c); + } + else if (c=='\'') hwritef(" ''''"); + else if (c<=0x7E) hwritef(" \'%c\'",c); /* printable ASCII */ + else if (option_utf8) { hwritef(" \'"); @+ hwrite_utf8(c); @+ hwritec('\'');@+} + else if (option_hex) hwritef(" 0x%04X",c); + else hwritef(" %u",c); +} +@ + +\getcode +@= +#define @[HGET_UTF8C(X)@] (X)=HGET8;@+ if ((X&0xC0)!=0x80) \ + QUIT(@["UTF8 continuation byte expected at " SIZE_F " got 0x%02X\n"@],hpos-hstart-1,X)@; + +uint32_t hget_utf8(void) +{ uint8_t a; + a=HGET8; + if (a<0x80) return a; + else + { if ((a&0xE0)==0xC0) @/ + { uint8_t b; @+ HGET_UTF8C(b); + return ((a&~0xE0)<<6)+(b&~0xC0); + } + else if ((a&0xF0)==0xE0) @/ + { uint8_t b,c; @+ HGET_UTF8C(b); @+ HGET_UTF8C(c); + return ((a&~0xF0)<<12)+((b&~0xC0)<<6)+(c&~0xC0); + } + else if ((a&0xF8)==0xF0) @/ + { uint8_t b,c,d; @+ HGET_UTF8C(b); @+ HGET_UTF8C(c); @+ HGET_UTF8C(d); + return ((a&~0xF8)<<18)@|+ ((b&~0xC0)<<12)+((c&~0xC0)<<6)+(d&~0xC0); + } + else QUIT("UTF8 byte sequence expected"); + } +} +@ +\putcode +@= +void hput_utf8(uint32_t c) +{ @+HPUTX(4); + if (c<0x80) + HPUT8(c); + else if (c<0x800) + { HPUT8(0xC0|(c>>6));@+ HPUT8(0x80|(c&0x3F));@+ } + else if (c<0x10000)@/ + { HPUT8(0xE0|(c>>12)); HPUT8(0x80|((c>>6)&0x3F));@+ HPUT8(0x80|(c&0x3F)); } + else if (c<0x200000)@/ + { HPUT8(0xF0|(c>>18));@+ HPUT8(0x80|((c>>12)&0x3F)); + HPUT8(0x80|((c>>6)&0x3F));@+ HPUT8(0x80|(c&0x3F)); } + else + RNG("character code",c,0,0x1FFFFF); +} +@ + +\subsection{Floating Point Numbers} +You know a floating point numbers\index{floating point number} when you see it because it features a radix\index{radix point} point. +The optional exponent\index{exponent} allows you to ``float'' the point. + +\readcode +@s FPNUM symbol +@s number symbol + +@= +%token FPNUM +%type number +@ +@= +::@=[+-]?[0-9]+\.[0-9]+(e[+-]?[0-9])?@> :< SCAN_DECFLOAT; return FPNUM; >: +@ + +The layout of floating point variables of type |double| +or |float| typically follows the IEEE754\index{IEEE754} standard\cite{IEEE754-1985}\cite{IEEE754-2008}. +We use the following definitions: + +\index{float32 t+\&{float32\_t}} +\index{float64 t+\&{float64\_t}} + +@= + +#define FLT_M_BITS 23 +#define FLT_E_BITS 8 +#define FLT_EXCESS 127 + +#define DBL_M_BITS 52 +#define DBL_E_BITS 11 +#define DBL_EXCESS 1023 + +@ + +@s float32_t int +@s float64_t int + +We expect a variable of type |float64_t| to have a binary representation using 64 bit. +The most significant bit is the sign bit, then follow $|DBL_E_BITS|=11$ bits for +the exponent\index{exponent}, and $|DBL_M_BITS|=52$ bits for the mantissa\index{mantissa}. +The sign\index{sign bit} bit is 1 for a negative number and 0 for a positive number. +A floating point number is stored in normalized\index{normalization} form which means that +the mantissa is shifted such that it has exactly 52+1 bit not counting leading zeros. +The leading bit is then always 1 and there is no need to store it. So 52 bits suffice. +To store the exponent, the excess $q=1023$ is added and the result is stored as an +unsigned 11 bit number. +For example if we regard the exponent bits and the mantissa bits as unsigned binary numbers +$e$ and $m$ then the absolute value of such a floating point number can be expressed +as $(1+m*2^{-52})\cdot2^{e-1023}$. +We make similar assumptions about variables of type |float32_t| using the constants as defined above. + + + + + +To convert the decimal representation of a floating point number to binary values of type |float64_t|, +we use a \CEE\ library function. + +@= +#define SCAN_DECFLOAT @[yylval.f=atof(yytext)@] +@ + +When the parser expects a floating point number and gets an integer number, +it converts it. So whenever in the long format a floating point number is expected, +an integer number will do as well. + +@= +number: UNSIGNED {$$=(float64_t)$1; } | SIGNED {$$=(float64_t)$1; } | FPNUM; +@ + +Unfortunately the decimal representation is not optimal for floating point numbers +since even simple numbers in decimal notation like $0.1$ do not have an exact +representation as a binary floating point number. +So if we want a notation that allows an exact representation +of binary floating point numbers, we must use a hexadecimal\index{hexadecimal} representation. +Hexadecimal floating point numbers start with an optional sign, then as usual the two characters ``{\tt 0x}'', +then follows a sequence of hex digits, a radix point, more hex digits, and an optional exponent. +The optional exponent starts with the character ``{\tt x}'', followed by an optional sign, and some more +hex digits. The hexadecimal exponent is given as a base 16 number and it is interpreted as an exponent +with the base 16. As an example an exponent of ``{\tt x10}'', would multiply the mantissa by $16^{16}$. +In other words it would shift any mantissa 16 hexadecimal digits to the left. Here are the exact rules: + +@= +::@=[+-]?0x{HEX}+\.{HEX}+(x[+-]?{HEX}+)?@> :< SCAN_HEXFLOAT; return FPNUM; >: +@ + +@= +#define SCAN_HEXFLOAT @[yylval.f=xtof(yytext)@] +@ +There is no function in the \CEE\ library for hexadecimal floating point notation +so we have to write our own conversion routine. +The function |xtof| converts a string matching the above regular expression to +its binary representation. Its outline is very simple: + +@= + +float64_t xtof(char *x) +{ int sign, digits, exp; + uint64_t mantissa=0; + DBG(DBGFLOAT,"converting %s:\n",x); + @@; + x=x+2; /* skip ``\.{0x}'' */ + @@; + @@; + @@; + @@; +} +@ + +Now the pieces: + +@= + if (*x=='-') { sign=-1;@+ x++;@+ } + else if (*x=='+') { sign=+1;@+ x++;@+ } + else @+sign=+1; + DBG(DBGFLOAT,"\tsign=%d\n",sign); +@ + +When we read the mantissa, we use the temporary variable |mantissa|, keep track +of the number of digits, and adjust the exponent while reading the fractional part. +@= + digits=0; + while (*x=='0') x++; /*ignore leading zeros*/ + while (*x!='.')@/ + { mantissa=mantissa<<4; + if (*x<'A') mantissa=mantissa+*x-'0'; + else mantissa=mantissa+*x-'A'+10; + x++; + digits++; + } + x++; /* skip ``\.{.}'' */ + exp=0; + while (*x!=0 && *x!='x')@/ + { mantissa=mantissa<<4; + exp=exp-4; + if (*x<'A') mantissa=mantissa+*x-'0'; + else mantissa=mantissa+*x-'A'+10; + x++; + digits++; + } + DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp); +@ + +To normalize the mantissa, first we shift it to place exactly one nonzero hexadecimal +digit to the left of the radix point. Then we shift it right bit-wise until there is +just a single 1 bit to the left of the radix point. +To compensate for the shifting, we adjust the exponent accordingly. +Finally we remove the most significant bit because it is +not stored. + +@= +if (mantissa==0) return 0.0; +{ int s; + s = digits-DBL_M_BITS/4; + if (s>1) + mantissa=mantissa>>(4*(s-1)); + else if (s<1) + mantissa=mantissa<<(4*(1-s)); + exp=exp+4*(digits-1); + DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp); + while ((mantissa>>DBL_M_BITS)>1)@/ { mantissa=mantissa>>1; @+ exp++;@+ } + DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 ", exp=%d\n",@|digits,mantissa,exp); + mantissa=mantissa&~((uint64_t)1<= + if (*x=='x')@/ + { int s; + x++; /* skip the ``\.{x}'' */ + if (*x=='-') {s=-1;@+x++;@+} + else if (*x=='+') {s=+1;@+x++;@+} + else s=+1; + DBG(DBGFLOAT,"\texpsign=%d\n",s); + DBG(DBGFLOAT,"\texp=%d\n",exp); + while (*x!=0 ) + { if (*x<'A') exp=exp+4*s*(*x-'0'); + else exp=exp+4*s*(*x-'A'+10); + x++; + DBG(DBGFLOAT,"\texp=%d\n",exp); + } + } + RNG("Floating point exponent",@|exp,-DBL_EXCESS,DBL_EXCESS); +@ + +To assemble the binary representation, we use a |union| of a |float64_t| and |uint64_t|. + + +@= +{ union {@+float64_t d; @+uint64_t bits; @+} u; + if (sign<0) sign=1;@+ else@+ sign=0; /* the sign bit */ + exp=exp+DBL_EXCESS; /* the exponent bits */ + u.bits=((uint64_t)sign<<63)@/ + | ((uint64_t)exp<= +#define MAX_HEX_DIGITS 12 +void hwrite_float64(float64_t d) +{ uint64_t bits, mantissa; + int exp, digits; + hwritec(' '); + if (floor(d)==d) + { hwritef("%d",(int)d);@+ return;@+} + if (floor(10000.0*d)==10000.0*d) + { hwritef("%g",d); @+return;@+} + DBG(DBGFLOAT,"Writing hexadecimal float %f\n",d); + if (d<0) { hwritec('-');@+ d=-d;@+} + hwritef("0x"); + @@; + if (exp>MAX_HEX_DIGITS) + @@; + else if (exp>=0) @@; + else @@; +} +@ + +The extraction just reverses the creation of the binary representation. + +@= +{ union {@+float64_t d; @+ uint64_t bits; @+} u; + u.d=d; @+ bits=u.bits; +} + mantissa= bits&(((uint64_t)1<>DBL_M_BITS)&((1<= + { int r; + if (exp>=0) + { r= exp%4; + if (r>0) + { mantissa=mantissa<0) + { mantissa=mantissa>>r; @+exp=exp+r; @+digits=digits-r;@+} + } + } + exp=exp/4; + DBG(DBGFLOAT,"\tdigits=%d mantissa=0x%" PRIx64 " hex exp=%d\n",@|digits,mantissa,exp); +@ + +In preparation for writing, +we shift the mantissa to the left so that the leftmost hexadecimal +digit of it will occupy the 4 leftmost bits of the variable |bits| . + +@= + mantissa=mantissa<<(64-DBL_M_BITS-4); /* move leading digit to leftmost nibble */ +@ + +If the exponent is larger than |MAX_HEX_DIGITS|, we need to +use an exponent even if the mantissa uses only a few digits. +When we use an exponent, we always write exactly one digit preceding the radix point. + +@= +{ DBG(DBGFLOAT,"writing large number\n"); + hwritef("%X.",(uint8_t)(mantissa>>60)); + mantissa=mantissa<<4; + do { + hwritef("%X",(uint8_t)(mantissa>>DBL_M_BITS)&0xF); + mantissa=mantissa<<4; + } while (mantissa!=0); + hwritef("x+%X",exp); +} +@ +If the exponent is small and non negative, we can write the +number without an exponent by writing the radix point at the +appropriate place. + @= + { DBG(DBGFLOAT,"writing medium number\n"); + do { + hwritef("%X",(uint8_t)(mantissa>>60)); + mantissa=mantissa<<4; + if (exp--==0) hwritec('.'); + } while (mantissa!=0 || exp>=-1); + } +@ +Last non least, we write numbers that would require additional zeros after the +radix point with an exponent, because it keeps the mantissa shorter. +@= + { DBG(DBGFLOAT,"writing small number\n"); + hwritef("%X.",(uint8_t)(mantissa>>60)); + mantissa=mantissa<<4; + do { + hwritef("%X",(uint8_t)(mantissa>>60)); + mantissa=mantissa<<4; + } while (mantissa!=0); + hwritef("x-%X",-exp); + } +@ + +Compared to the complications of long format floating point numbers, +the short format is very simple because we just use the binary representation. +Since 32 bit floating point numbers offer sufficient precision we use only +the |float32_t| type. +It is however not possible to just write |HPUT32(d)| for a |float32_t| variable |d| +or |HPUT32((uint32_t)d)| because in the \CEE\ language this would imply +rounding the floating point number to the nearest integer. +But we have seen how to convert floating point values to bit pattern before. + +@= +void hput_float32(float32_t d) +{ union {@+float32_t d; @+ uint32_t bits; @+} u; + u.d=d; @+ HPUT32(u.bits); +} +@ + +@= +float32_t hget_float32(void) +{ union {@+float32_t d; @+ uint32_t bits; @+} u; + HGET32(u.bits); + return u.d; +} +@ + +\subsection{Fixed Point Numbers} +\TeX\ internally represents most real numbers as fixed\index{fixed point number} point numbers or ``scaled integers''\index{scaled integer}. +The type {\bf scaled\_t} is defined as a signed 32 bit integer, but we consider it as a fixed point number +with the binary radix point just in the middle with sixteen bits before and sixteen bits after it. +To convert an integer into a scaled number, we multiply it by |ONE|; to convert a floating point number +into a scaled number, we multiply it by |ONE| and |ROUND| the result to the nearest integer; +to convert a scaled number to a floating point number we divide it by |(float64_t)ONE|. + +\noindent +@= +typedef int32_t scaled_t; +#define ONE ((scaled_t)(1<<16)) +@ + +@= +#define ROUND(X) ((int)((X)>=0.0?floor((X)+0.5):ceil((X)-0.5))) +@ + +\writecode +@= +void hwrite_scaled(scaled_t x) +{ hwrite_float64(x/(float64_t)ONE); +} +@ + +\subsection{Dimensions} +In the long format, +the dimensions\index{dimension} of characters, boxes, and other things can be given +in three units: \.{pt}, \.{in}, and \.{mm}. + +\readcode +@s PT symbol +@s MM symbol +@s INCH symbol +@s dimension symbol +@s DIMEN symbol +@= +%token DIMEN "dimen" +%token PT "pt" +%token MM "mm" +%token INCH "in" +%type dimension +@ + +@= +::@=dimen@> :< return DIMEN; >: +::@=pt@> :< return PT; >: +::@=mm@> :< return MM; >: +::@=in@> :< return INCH; >: +@ + +The unit \.{pt} is a printers point\index{point}\index{pt+{\tt pt}}. +The unit ``\.{in}'' stands for inches\index{inch}\index{in+{\tt in}} and we have $1\.{in}= 72.27\,\.{pt}$. +The unit ``\.{mm}'' stands for millimeter\index{millimeter}\index{mm+{\tt mm}} and we have $1\.{in}= 25.4\,\.{mm}$. + +The definition of a printers\index{printers point} point given above follows the definition used in +\TeX\ which is slightly larger than the official definition of a printer's +point which was defined to equal exactly 0.013837\.{in} by the American Typefounders +Association in~1886\cite{DK:texbook}. + +We follow the tradition of \TeX\ and +store dimensions as ``scaled points''\index{scaled point} that is a dimension of $d$ points is +stored as $d\cdot2^{16}$ rounded to the nearest integer. +The maximum absolute value of a dimension is $(2^{30}-1)$ scaled points. + +@= +typedef scaled_t dimen_t; +#define MAX_DIMEN ((dimen_t)(0x3FFFFFFF)) +@ + +@= +dimension: number PT @|{$$=ROUND($1*ONE); RNG("Dimension",$$,-MAX_DIMEN,MAX_DIMEN); } + | number INCH @|{$$=ROUND($1*ONE*72.27); RNG("Dimension",$$,-MAX_DIMEN,MAX_DIMEN);@+} + | number MM @|{$$=ROUND($1*ONE*(72.27/25.4)); RNG("Dimension",$$,-MAX_DIMEN,MAX_DIMEN);@+}; +@ + +When \.{stretch} is writing dimensions in the long format, +for simplicity it always uses the unit ``\.{pt}''. +\writecode +@= +void hwrite_dimension(dimen_t x) +{ hwrite_scaled(x); + hwritef("pt"); +} +@ + +In the short format, dimensions are stored as 32 bit scaled point values without conversion. +\getcode +@= +void hget_dimen(uint8_t a) +{ if (INFO(a)==b000) + {uint8_t r; r=HGET8; REF(dimen_kind,r); hwrite_ref(r);} + else + { uint32_t d; HGET32(d); hwrite_dimension(d); } +} +@ + +\putcode +@= + +uint8_t hput_dimen(dimen_t d) +{ HPUT32(d); + return TAG(dimen_kind, b001); +} +@ + + + +\subsection{Extended Dimensions}\index{extended dimension}\index{hsize+{\tt hsize}}\index{vsize+{\tt vsize}} +The dimension that is probably used most frequently in a \TeX\ file is {\tt hsize}: +the ho\-ri\-zon\-tal size of a line of text. Common are also assignments +like \.{\\hsize=0.5\\hsize} \.{\\advance\\hsize by -10pt}, for example to +get two columns with lines almost half as wide as usual, leaving a small gap +between left and right column. Similar considerations apply to {\tt vsize}. + +Because we aim at a reflowable format for \TeX\ output, we have to postpone +such computations until the values of \.{hsize} and \.{vsize} are known in the viewer. +Until then, we do symbolic computations on linear functions\index{linear function} of \.{hsize} and \.{vsize}. +We call such a linear function $w+h\cdot\.{hsize}+v\cdot\.{vsize}$ +an extended dimension and represent it by the three numbers $w$, $h$, and $v$. + +@= +typedef struct {@+ +dimen_t w; @+ float32_t h, v; @+ +} xdimen_t; +@ +Since very often a component of an extended dimension is zero, we +store in the short format only the nonzero components and use the +info bits to mark them: |b100| implies $|w|\ne0$, +|b010| implies $|h|\ne 0$, and |b001| implies $|v|\ne 0$. + +\readcode +@s XDIMEN symbol +@s xdimen symbol +@s xdimen_node symbol +@s H symbol +@s V symbol +@= +%token XDIMEN "xdimen" +%token H "h" +%token V "v" +%type xdimen +@ +@= +::@=xdimen@> :< return XDIMEN; >: +::@=h@> :< return H; >: +::@=v@> :< return V; >: +@ + + +@= +xdimen: dimension number H number V { $$.w=$1; @+$$.h=$2; @+$$.v=$4; } + | dimension number H { $$.w=$1; @+$$.h=$2; @+$$.v=0.0; } + | dimension number V { $$.w=$1; @+$$.h=0.0; @+$$.v=$2; } + | dimension { $$.w=$1; @+$$.h=0.0; @+$$.v=0.0; }; + + + +xdimen_node: start XDIMEN xdimen END { hput_tags($1,hput_xdimen(&($3))); }; +@ + +\writecode +@= +void hwrite_xdimen(xdimen_t *x) +{ hwrite_dimension(x->w); + if (x->h!=0.0) {hwrite_float64(x->h); @+hwritec('h');@+} + if (x->v!=0.0) {hwrite_float64(x->v); @+hwritec('v');@+} +} + +void hwrite_xdimen_node(xdimen_t *x) +{ hwrite_start(); hwritef("xdimen"); hwrite_xdimen(x); hwrite_end();} +@ + +\getcode + +@= +#define @[HGET_XDIMEN(I,X)@] \ + if((I)&b100) HGET32((X).w);@+ else (X).w=0;\ + if((I)&b010) (X).h=hget_float32(); @+ else (X).h=0.0;\ + if((I)&b001) (X).v=hget_float32(); @+else (X).v=0.0; +@ + +@= +void hget_xdimen(uint8_t a, xdimen_t *x) +{ switch(a) + { + case TAG(xdimen_kind,b001): HGET_XDIMEN(b001,*x);@+break; + case TAG(xdimen_kind,b010): HGET_XDIMEN(b010,*x);@+break; + case TAG(xdimen_kind,b011): HGET_XDIMEN(b011,*x);@+break; + case TAG(xdimen_kind,b100): HGET_XDIMEN(b100,*x);@+break; + case TAG(xdimen_kind,b101): HGET_XDIMEN(b101,*x);@+break; + case TAG(xdimen_kind,b110): HGET_XDIMEN(b110,*x);@+break; + case TAG(xdimen_kind,b111): HGET_XDIMEN(b111,*x);@+break; + default: + QUIT("Extent expected got [%s,%d]",NAME(a),INFO(a)); + } + } +@ + +Note that the info value |b000|, usually indicating a reference, +is not supported for extended dimensions. +Most nodes that need an extended dimension offer the opportunity to give +a reference directly without the start and end byte. An exception is the glue node, +but glue nodes that need an extended width are rare. + +@= +void hget_xdimen_node(xdimen_t *x) +{ @@; + if (KIND(a)==xdimen_kind) + hget_xdimen(a,x); + else + QUIT("Extent expected at 0x%x got %s",node_pos,NAME(a)); + @@; +} +@ + + + +\putcode +@= +uint8_t hput_xdimen(xdimen_t *x) +{ info_t info=b000; + if (x->w==0 && x->h==0.0 && x->v==0.0){ HPUT32(0); @+info|=b100; @+} + else + { if (x->w!=0) { HPUT32(x->w); @+info|=b100; @+} + if (x->h!=0.0) { hput_float32(x->h); @+info|=b010; @+} + if (x->v!=0.0) { hput_float32(x->v); @+info|=b001; @+} + } + return TAG(xdimen_kind,info); +} +void hput_xdimen_node(xdimen_t *x) +{ uint32_t p=hpos++-hstart; + hput_tags(p, hput_xdimen(x)); +} + + +@ + + + +\subsection{Stretch and Shrink}\label{stretch} +In section~\secref{glue}, we will consider glue\index{glue} which +is something that can stretch and shrink. +The stretchability\index{stretchability} and shrinkability\index{shrinkability} of the +glue can be given in ``\.{pt}'' like a dimension, +but there are three more units: \.{fil}, \.{fill}, and \.{filll}. +A glue with a stretchability of $1\,\hbox{\tt fil}$ will stretch infinitely more +than a glue with a stretchability of $1\,\hbox{\tt pt}$. So if you stretch both glues +together, the first glue will do all the stretching and the latter will not stretch +at all. The ``\.{fil}'' glue has simply a higher order of infinity. +You might guess that ``\.{fill}'' glue and ``\.{filll}'' glue have even higher +orders of infinite stretchability. +The order of infinity is 0 for \.{pt}, 1 for \.{fil}, 2 for \.{fill}, and 3 for \.{filll}. + +The internal representation of a stretch is a variable of type |stretch_t|. +It stores the floating point value and the order of infinity separate as a |float64_t| and a |uint8_t|. + + +The short format tries to be space efficient and because it is not necessary to give the +stretchability with a precision exceeding about six decimal digits, +we use a single 32 bit floating point value. +To write a |float32_t| value and an order value as one 32 bit value, +we round the two lowest bit of the |float32_t| variable to zero +using ``round to even'' and store the order of infinity in these bits. +We define a union type \&{stch\_t} to simplify conversion. + +@= +typedef enum { @+ normal_o=0, fil_o=1, fill_o=2, filll_o=3@+} order_t; +typedef struct {@+ float64_t f;@+ order_t o; @+} stretch_t; +typedef union {@+float32_t f; @+ uint32_t u; @+} stch_t; +@ + +\putcode +@= +void hput_stretch(stretch_t *s) +{ uint32_t mantissa, lowbits, sign, exponent; + stch_t st; + st.f=s->f; + DBG(DBGFLOAT,"joining %f->%f(0x%X),%d:",s->f,st.f,st.u,s->o); + mantissa = st.u &(((uint32_t)1<>FLT_M_BITS)&(((uint32_t)1<= ((uint32_t)1<>1; + } + break; + } + DBG(DBGFLOAT," round s=%d e=0x%x m=0x%x",sign, exponent, mantissa); + st.u=sign| (exponent<o; + DBG(DBGFLOAT,"float %f hex 0x%x\n",st.f, st.u); + HPUT32(st.u); +} +@ + +\getcode +@= +#define @[HGET_STRETCH(S)@] { stch_t st; @+ HGET32(st.u);@+ S.o=st.u&3; st.u&=~3; S.f=st.f; @+} +@ + +\readcode +@s FIL symbol +@s FILL symbol +@s FILLL symbol +@s order symbol + +@= +%token FIL "fil" +%token FILL "fill" +%token FILLL "filll" +%type stretch +%type order +@ + +@= +::@=fil@> :< return FIL; >: +::@=fill@> :< return FILL; >: +::@=filll@> :< return FILLL; >: +@ + +@s stretch symbol +@s stretch_t int +@= + +order: PT {$$=normal_o;} | FIL {$$=fil_o;} @+| FILL {$$=fill_o;} @+| FILLL {$$=filll_o;}; + +stretch: number order { $$.f=$1; $$.o=$2; }; +@ + +\writecode + +@= +void hwrite_order(order_t o) +{ switch (o) + { case normal_o: hwritef("pt"); @+break; + case fil_o: hwritef("fil"); @+break; + case fill_o: hwritef("fill"); @+break; + case filll_o: hwritef("filll"); @+break; + default: QUIT("Illegal order %d",o); @+ break; + } +} + +void hwrite_stretch(stretch_t *s) +{ hwrite_float64(s->f); + hwrite_order(s->o); +} +@ + +\section{Simple Nodes}\hascode +\label{simple} +\subsection{Penalties} +Penalties\index{penalty} are very simple nodes. They specify the cost of breaking a +line or page at the present position. For the internal representation +we use an |int32_t|. The full range of integers is, however, not +used. Instead penalties must be between -20000 and +20000. +(\TeX\ specifies a range of -10000 to +10000, but plain \TeX\ uses the value -20000 +when it defines the supereject control sequence.) +The more general node is called an integer node; +it shares the same kind-value |int_kind=penalty_kind| +but allows the full range of values. +The info value of a penalty node is 1 or 2 and indicates the number of bytes +used to store the integer. The info value 4 can be used for general +integers (see section~\secref{definitions}) that need four byte of storage. + +\readcode +@s PENALTY symbol +@s INTEGER symbol +@s penalty symbol +@= +%token PENALTY "penalty" +%token INTEGER "int" +%type penalty +@ + +@= +::@=penalty@> :< return PENALTY; >: +::@=int@> :< return INTEGER; >: +@ + +@= +penalty: integer {RNG("Penalty",$1,-20000,+20000);$$=$1;}; +content_node: start PENALTY penalty END { hput_tags($1,hput_int($3));@+}; +@ + +\getcode +@= +@t\1\kern1em@>case TAG(penalty_kind,1): @+{int32_t p;@+ HGET_PENALTY(1,p);@+} @+break; +case TAG(penalty_kind,2): @+{int32_t p;@+ HGET_PENALTY(2,p);@+} @+break; +@ + +@= +#define @[HGET_PENALTY(I,P)@] \ +if (I==1) {int8_t n=HGET8; @+P=n;@+ } \ +else {int16_t n;@+ HGET16(n);@+RNG("Penalty",n,-20000,+20000); @+ P=n; @+}\ +hwrite_signed(P); +@ + +\putcode +@= +uint8_t hput_int(int32_t n) +{ info_t info; + if (n>=0) @/ + { @+if (n<0x80) { @+HPUT8(n); @+info=1;@+ } + else if (n<0x8000) {@+ HPUT16(n);@+ info=2;@+ } + else {@+ HPUT32(n);@+ info=4;@+ } + } + else@/ + {@+ if (n>=-0x80) {@+ HPUT8(n);@+ info=1;@+ } + else if (n>=-0x8000) {@+ HPUT16(n);@+ info=2;@+ } + else {@+ HPUT32(n);@+ info=4;@+ } + } + return TAG(int_kind,info); +} +@ + + + + +\subsection{Languages} +To render a \HINT\ file on screen, information about the language is not necessary. +Knowing the language is, however, very important for language translation and +text to speech conversion which makes texts accessible to the visually-impaired. +For this reason, \HINT\ offers the opportunity to add this information +and encourages authors to supply this information. + +Language information by itself is not sufficient to decode text. It must be supplemented +by information about the character encoding (see section~\secref{fonts}). + +To represent language information, the world wide web has set universally +accepted standards. The Internet Engineering Task Force IETF has defined tags for identifying +languages\cite{rfc5646}: short strings like ``en'' for English +or ``de'' for Deutsch, and longer ones like ``sl-IT-nedis'', for the specific variant of +the Nadiza dialect of Slovenian that is spoken in Italy. +We assume that any \HINT\ file +will contain only a small number of different languages and all language nodes can be +encoded using a reference to a predefined node from the +definition section (see section~\secref{reference}). +In the definition section, a language node will just +contain the language tag as given in~\cite{iana:language} (see section~\secref{definitions}). + +\readcode +@s LANGUAGE symbol +@s language symbol + +@= +%token LANGUAGE "language" +@ + +@= +::@=language@> :< return LANGUAGE; >: +@ + +When encoding language nodes in the short format, +we use the info value |b000| for language nodes in the definition section +and for language nodes in the content section that contain just a one-byte +reference (see section~\secref{reference}). +We use the info value |1| to |7| as a shorthand for +the references {\tt *0} and {\tt *6} to the predefined language nodes. + + +\goodbreak +\vbox{\getcode\vskip -\baselineskip\writecode} +@= +@t\1\kern1em@>case TAG(language_kind,1): REF(language_kind,0); @+hwrite_ref(0); @+break; +case TAG(language_kind,2): REF(language_kind,1); @+hwrite_ref(1); @+break; +case TAG(language_kind,3): REF(language_kind,2); @+hwrite_ref(2); @+break; +case TAG(language_kind,4): REF(language_kind,3); @+hwrite_ref(3); @+break; +case TAG(language_kind,5): REF(language_kind,4); @+hwrite_ref(4); @+break; +case TAG(language_kind,6): REF(language_kind,5); @+hwrite_ref(5); @+break; +case TAG(language_kind,7): REF(language_kind,6); @+hwrite_ref(6); @+break; +@ + +\putcode +@= +uint8_t hput_language(uint8_t n) +{ if (n<7) return TAG(language_kind,n+1); + HPUT8(n); return TAG(language_kind,0); +} +@ + + + +\subsection{Rules} +Rules\index{rule} are simply black rectangles having a height, a depth, and a +width. All of these dimensions can also be negative but a rule will +not be visible unless its width is positive and its height plus depth +is positive. + +As a specialty, rules can have ``running dimensions''\index{running dimension}. If any of the +three dimensions is a running dimension, its actual value will be +determined by running the rule up to the boundary of the innermost +enclosing box. The width is never running in an horizontal\index{horizontal list} list; the +height and depth are never running in a vertical\index{vertical list} list. In the long +format, we use a vertical bar ``{\tt \VB}'' or a horizontal bar +``{\tt \_}'' (underscore character) to indicate a running +dimension. Of course the vertical bar is meant to indicate a running +height or depth while the horizontal bar stands for a running +width. The parser, however, makes no distinction between the two and +you can use either of them. In the short format, we follow \TeX\ and +implement a running dimension by using the special value +$-2^{30}=|0xC0000000|$. + + +@= +#define RUNNING_DIMEN 0xC0000000 +@ + +It could have been possible to allow extended dimensions in a rule node, +but in most circumstances, the mechanism of running dimensions is sufficient +and simpler to use. If a rule is needed that requires an extended dimension as +its length, it is always possible to put it inside a suitable box and use a +running dimension. + + +To make the short format encoding more compact, the first info bit +|b100| will be zero to indicate a running height, bit |b010| will be +zero to indicate a running depth, and bit |b001| will be zero to +indicate a running width. + +Because leaders\index{leaders} (see section~\secref{leaders}) may contain a rule +node, we also provide functions to read and write a complete rule +node. While parsing the symbol ``{\sl rule\/}'' will just initialize a variable of type +\&{rule\_t} (the writing is done with a separate routine), +parsing a {\sl rule\_node\/} will always include writing it. + +% Currently no predefined rules. +%Further, a {\sl rule\_node} will permit the +%use of a predefined rule (see section~\secref{reference}), + + +@= +typedef struct {@+ +dimen_t h,d,w; @+ +} rule_t; +@ + +\readcode +@s RULE symbol +@s RUNNING symbol +@s rule_dimension symbol +@s rule symbol +@s rule_node symbol +@= +%token RULE "rule" +%token RUNNING "|" +%type rule_dimension +%type rule +@ + +@= +::@=rule@> :< return RULE; >: +::@="|"@> :< return RUNNING; >: +::@="_"@> :< return RUNNING; >: +@ + +@= +rule_dimension: dimension@+ | RUNNING {$$=RUNNING_DIMEN;}; +rule: rule_dimension rule_dimension rule_dimension @/ + { $$.h=$1; @+ $$.d=$2; @+ $$.w=$3; + if ($3==RUNNING_DIMEN && ($1==RUNNING_DIMEN || $2==RUNNING_DIMEN)) + QUIT("Incompatible running dimensions 0x%x 0x%x 0x%x",@|$1,$2,$3); }; +rule_node: start RULE rule END { hput_tags($1,hput_rule(&($3))); }; +content_node: rule_node; +@ + +\writecode +@= +static void hwrite_rule_dimension(dimen_t d, char c) +{ @+if (d==RUNNING_DIMEN) hwritef(" %c",c); + else hwrite_dimension(d); +} + +void hwrite_rule(rule_t *r) +{ @+hwrite_rule_dimension(r->h,'|'); + hwrite_rule_dimension(r->d,'|'); + hwrite_rule_dimension(r->w,'_'); +} +@ +\getcode +@= +@t\1\kern1em@> +case TAG(rule_kind,b011): {rule_t r;@+ HGET_RULE(b011,r); @+hwrite_rule(&(r));@+ } @+break; +case TAG(rule_kind,b101): {rule_t r;@+ HGET_RULE(b101,r); @+hwrite_rule(&(r));@+ } @+break; +case TAG(rule_kind,b001): {rule_t r;@+ HGET_RULE(b001,r); @+hwrite_rule(&(r));@+ } @+break; +case TAG(rule_kind,b110): {rule_t r;@+ HGET_RULE(b110,r); @+hwrite_rule(&(r));@+ } @+break; +case TAG(rule_kind,b111): {rule_t r;@+ HGET_RULE(b111,r); @+hwrite_rule(&(r));@+ } @+break; +@ + +@= +#define @[HGET_RULE(I,R)@]@/\ +if ((I)&b100) HGET32((R).h); @+else (R).h=RUNNING_DIMEN;\ +if ((I)&b010) HGET32((R).d); @+else (R).d=RUNNING_DIMEN;\ +if ((I)&b001) HGET32((R).w); @+else (R).w=RUNNING_DIMEN; +@ + +@= +void hget_rule_node(void) +{ @@; + if (KIND(a)==rule_kind) @/ + { @+rule_t r; @+HGET_RULE(INFO(a),r); @/ + hwrite_start();@+ hwritef("rule"); @+hwrite_rule(&r); @+hwrite_end(); + } + else + QUIT("Rule expected at 0x%x got %s",node_pos,NAME(a)); + @@; +} +@ + +\putcode +@= +uint8_t hput_rule(rule_t *r) +{ info_t info=b000; + if (r->h!=RUNNING_DIMEN) { HPUT32(r->h); @+info|=b100; @+} + if (r->d!=RUNNING_DIMEN) { HPUT32(r->d); @+info|=b010; @+} + if (r->w!=RUNNING_DIMEN) { HPUT32(r->w); @+info|=b001; @+} + return TAG(rule_kind,info); +} +@ + + +\subsection{Kerns} +A kern\index{kern} is a bit of white space with a certain length. If the kern is part of a +horizontal list, the length is measured in the horizontal direction, +if it is part of a vertical list, it is measured in the vertical +direction. The length of a kern is mostly given as a dimension +but provisions are made to use extended dimensions as well. + +The typical +use of a kern is its insertion between two characters to make the natural +distance between them a bit wider or smaller. In the latter case, the kern +has a negative length. The typographic optimization just described is called +``kerning'' and has given the kern node its name. +Kerns inserted from font information or math mode calculations are normal kerns, +while kerns inserted from \TeX's {\tt \BS kern} or {\tt \BS/} +commands are explicit kerns. +Kern nodes do not disappear at a line break unless they are explicit\index{explicit kern}. + +In the long format, explicit kerns are marked with an ``!'' sign +and in the short format with the |b100| info bit. +The two low order info bits are: 0 for a reference to a dimension, 1 for a reference to +an extended dimension, 2 for an immediate dimension, and 3 for an immediate extended dimension node. +To distinguish in the long format between a reference to a dimension and a reference to an extended dimension, +the latter is prefixed with the keyword ``{\tt xdimen}'' (see section~\secref{reference}). + +@= +typedef struct {@+ +bool x;@+ +xdimen_t d;@+ +} kern_t; +@ + +\readcode +@s KERN symbol +@s EXPLICIT symbol +@s kern symbol +@s explicit symbol +@= +%token KERN "kern" +%token EXPLICIT "!" +%type explicit +%type kern +@ + +@= +::@=kern@> :< return KERN; >: +::@=!@> :< return EXPLICIT; >: +@ + +@= +explicit: {$$=false;} @+| EXPLICIT {$$=true;}; +kern: explicit xdimen {$$.x=$1; $$.d=$2;}; +content_node: start KERN kern END { hput_tags($1,hput_kern(&($3)));} +@ + +\writecode +@= +void hwrite_explicit(bool x) +{ @+if (x) hwritef(" !"); @+} + +void hwrite_kern(kern_t *k) +{ @+hwrite_explicit(k->x); + if (k->d.h==0.0 && k->d.v==0.0 && k->d.w==0) hwrite_ref(zero_dimen_no); + else hwrite_xdimen(&(k->d)); +} +@ + + +\getcode +@= +@t\1\kern1em@> +case TAG(kern_kind,b010): @+ {@+kern_t k; @+HGET_KERN(b010,k);@+ } @+break; +case TAG(kern_kind,b011): @+ {@+kern_t k; @+HGET_KERN(b011,k);@+ } @+break; +case TAG(kern_kind,b110): @+ {@+kern_t k; @+HGET_KERN(b110,k);@+ } @+break; +case TAG(kern_kind,b111): @+ {@+kern_t k; @+HGET_KERN(b111,k);@+ } @+break; +@ + +@= +#define @[HGET_KERN(I,K)@] \ +K.x=(I)&b100;\ +if (((I)&b011)==2) {HGET32(K.d.w);@+ K.d.h=K.d.v=0.0;@+}\ +else if (((I)&b011)==3) hget_xdimen_node(&(K.d));\ +hwrite_kern(&k); +@ + +\putcode +@= +uint8_t hput_kern(kern_t *k) +{ info_t info; + if (k->x) info=b100; @+else info=b000; + if (k->d.h==0.0 && k->d.v==0.0) + { if (k->d.w==0) HPUT8(zero_dimen_no); + else {HPUT32(k->d.w); info=info|2;@+} + } + else {hput_xdimen_node(&(k->d));info=info|3;@+} + return TAG(kern_kind,info); +} +@ + + + +\subsection{Glue}\label{glue} + +%Glue considerations + +%So what are the cases: +%\itemize +%\item reference to a dimen (common) +%\item reference to a xdimen +%\item reference to a dimen plus and minus +%\item reference to a xdimen plus and minus +%\item reference to a dimen plus +%\item reference to a xdimen plus +%\item reference to a dimen minus +%\item reference to a xdimen minus +%\item dimen +%\item xdimen +%\item dimen plus and minus +%\item xdimen plus and minus (covers all other cases) +%\item dimen plus +%\item xdimen plus +%\item dimen minus +%\item xdimen minus +%\item plus and minus +%\item plus +%\item minus +%\item zero glue (rare, can be replaced by a reference to the zero glue) +%\item reference to a predefined glue (common) +%\enditemize +%This is a total of 21 cases. Can we use the info bits to specify 7 common +%cases and one catch all? First the use of an extended dimension in a glue +%is probably not very common. More typically is the use of a fill glue +%that extends to the boundaries of the enclosing box. + +%Here is the statistics for ctex: +%total 58937 glue entries +%total 49 defined glues (so 200 still available) +%There are three font specific glues defined for each font used in texts. +%The explicit glue nodes are the following: +%\itemize +%\item 35\% is predefined zero glue +%\item 30\% are 39 other predefined glue most of them less than 1% +%\item 8\% (4839) is one glue with 25pt pure stretch with order 0 +%\item 25\% (14746) is one glue with 100pt stretch and 10pt shrink with order 0 +%\item 2\% (1096) is one glue with 10pt no stretch and shrink +%\item 0\% (13) are 7 different glues with no stretch and shrink +%\item 0\% (3) different glues with width!=0 and some stretch of order 0 +%\item 0\% (27) 20 different glues with stretch and shrink +%\enditemize + +%Some more glue with 1fil is insider 55 leaders +%one vset has an extent 1 no stretch and shrink +%56 hset specify an extent 2 and 1 fil stretch + + +We have seen in section~\secref{stretch} how to deal with +stretchability\index{stretchability} and +shrinkability\index{shrinkability} and we will need this now. +Glue\index{glue} has a natural width---which in general can be an +extended dimension---and in addition it can stretch and shrink. It +might have been possible to allow an extended dimension also for the +stretch\-ability or shrink\-ability of a glue, but this seems of +little practical relevance and so simplicity won over generality. +Even with that restriction, it is an understatement to regard glue +nodes as "simple" nodes. +%, and we could equally well list them in +%section~\secref{composite} as composite nodes. + +To use the info bits in the short format wisely, I collected some +statistical data using the \TeX book as an example. It turns out that +about 99\% of all the 58937 glue nodes (not counting the interword +glues used inside texts) could be covered with only 43 predefined +glues. So this is by far the most common case; we reserve the info +value |b000| to cover it and postpone the description of such glue +nodes until we describe references in section~\secref{reference}. + +We expect the remaining cases to contribute not too much to the file +size, and hence, simplicity is a more important aspect than efficiency +when allocating the remaining info values. + +Looking at the glues in more detail, we find that the most common +cases are those where either one, two, or all three glue components +are zero. We use the two lowest bits to indicate the presence of a +nonzero stretchability or shrinkability and reserve the info values +|b001|, |b010|, and |b011| for those cases where the width of the glue +is zero. The zero glue, where all components are zero, is defined as +a fixed, predefined glue instead of reserving a special info value for +it. The cost of one extra byte when encoding it seems not too high a +price to pay. After reserving the info value |b111| for the most +general case of a glue, we have only three more info values left: +|b100|, |b101|, and |b110|. Keeping things simple implies using the +two lowest info bits---as before---to indicate a nonzero +stretchability or shrinkability. For the width, three choices remain: +using a reference to a dimension, using a reference to an extended +dimension, or using an immediate value. Since references to glues are +already supported, an immediate width seems best for glues that are +not frequently reused, avoiding the overhead of references. + +% It also makes parsing simpler because we avoid the confusion +% between references to dimensions +% and references to glues and references to extended dimensions. + +Here is a summary of the info bits and the implied layout +of glue nodes in the short format: +\itemize +\item |b000|: reference to a predefined glue +\item |b001|: zero width and nonzero shrinkability +\item |b010|: zero width and nonzero stretchability +\item |b011|: zero width and nonzero stretchability and shrinkability +\item |b100|: nonzero width +\item |b101|: nonzero width and nonzero shrinkability +\item |b110|: nonzero width and nonzero stretchability +\item |b111|: extended dimension and nonzero stretchability and shrinkability +\enditemize + + +@= +typedef struct {@+ +xdimen_t w; @+ +stretch_t p, m;@+ +} glue_t; +@ + + +To test for a zero glue, +we implement a macro: +@= +#define @[ZERO_GLUE(G)@] ((G).w.w==0 && (G).w.h==0.0 && (G).w.v==0.0 && (G).p.f==0.0 && (G).m.f==0.0) +@ + +Because other nodes (leaders, baselines, and fonts) +contain glue nodes as parameters, we provide functions +to read and write a complete glue node in the same way as we did +for rule nodes. +Further, such an internal {\sl glue\_node\/} has the special property that +in the short format a node for the zero glue might be omitted entirely. + +\readcode +@s GLUE symbol +@s glue symbol +@s glue_node symbol +@s PLUS symbol +@s MINUS symbol +@s plus symbol +@s minus symbol + +@= +%token GLUE "glue" +%token PLUS "plus" +%token MINUS "minus" +%type glue +%type glue_node +%type plus minus +@ + +@= +::@=glue@> :< return GLUE; >: +::@=plus@> :< return PLUS; >: +::@=minus@> :< return MINUS; >: +@ + +@= +plus: { $$.f=0.0; $$.o=0; } | PLUS stretch {$$=$2;}; +minus: { $$.f=0.0; $$.o=0; } | MINUS stretch {$$=$2;}; +glue: xdimen plus minus {$$.w=$1; $$.p=$2; $$.m=$3; }; +content_node: start GLUE glue END {if (ZERO_GLUE($3)) {HPUT8(zero_skip_no); + hput_tags($1,TAG(glue_kind,0)); } else hput_tags($1,hput_glue(&($3))); }; +glue_node: start GLUE glue END @/ + {@+ if (ZERO_GLUE($3)) { hpos--; $$=false;@+}@/ + else { hput_tags($1,hput_glue(&($3))); $$=true;@+}@+ }; +@ + +\writecode +@= +void hwrite_plus(stretch_t *p) +{ @+if (p->f!=0.0) { hwritef(" plus");@+hwrite_stretch(p); @+} +} +void hwrite_minus(stretch_t *m) +{@+ if (m->f!=0.0) { hwritef(" minus");@+hwrite_stretch(m); @+} +} + +void hwrite_glue(glue_t *g) +{ hwrite_xdimen(&(g->w)); @+ + hwrite_plus(&g->p); @+hwrite_minus(&g->m); +} + +void hwrite_ref_node(kind_t k, uint8_t n); +void hwrite_glue_node(glue_t *g) +{@+ + if (ZERO_GLUE(*g)) hwrite_ref_node(glue_kind,zero_skip_no); + else @+{ hwrite_start(); @+hwritef("glue"); @+hwrite_glue(g); @+hwrite_end();@+} +} +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(glue_kind,b001): { glue_t g;@+ HGET_GLUE(b001,g);@+ hwrite_glue(&g);@+}@+break; +case TAG(glue_kind,b010): { glue_t g;@+ HGET_GLUE(b010,g);@+ hwrite_glue(&g);@+}@+break; +case TAG(glue_kind,b011): { glue_t g;@+ HGET_GLUE(b011,g);@+ hwrite_glue(&g);@+}@+break; +case TAG(glue_kind,b100): { glue_t g;@+ HGET_GLUE(b100,g);@+ hwrite_glue(&g);@+}@+break; +case TAG(glue_kind,b101): { glue_t g;@+ HGET_GLUE(b101,g);@+ hwrite_glue(&g);@+}@+break; +case TAG(glue_kind,b110): { glue_t g;@+ HGET_GLUE(b110,g);@+ hwrite_glue(&g);@+}@+break; +case TAG(glue_kind,b111): { glue_t g;@+ HGET_GLUE(b111,g);@+ hwrite_glue(&g);@+}@+break; +@ + +@= +#define @[HGET_GLUE(I,G)@] {\ + if((I)!=b111) { if ((I)&b100) HGET32((G).w.w);@+ else (G).w.w=0;}\ + if((I)&b010) HGET_STRETCH((G).p) @+else (G).p.f=0.0, (G).p.o=0;\ + if((I)&b001) HGET_STRETCH((G).m) @+else (G).m.f=0.0, (G).m.o=0;\ + if((I)==b111) hget_xdimen_node(&((G).w)); else (G).w.h=(G).w.v=0.0;@+} +@ + +The |hget_glue_node| can cope with a glue node that is omitted and +will supply a zero glue instead. + +@= +void hget_glue_node(void) +{ @@; + if (KIND(a)!=glue_kind) + {@+ hpos--; hwrite_ref_node(glue_kind,zero_skip_no);@+return; @+} + if (INFO(a)==b000) + { uint8_t n=HGET8;@+ REF(glue_kind,n);@+hwrite_ref_node(glue_kind,n); @+} + else + { @+glue_t g; @+HGET_GLUE(INFO(a),g);@+ hwrite_glue_node(&g);@+} + @@; +} +@ + + +\putcode +@= +uint8_t hput_glue(glue_t *g) +{ info_t info=b000; + if (ZERO_GLUE(*g)) { HPUT8(zero_skip_no); @+ info=b000; } + else if ( (g->w.w==0 && g->w.h==0.0 && g->w.v==0.0)) + { if (g->p.f!=0.0) { hput_stretch(&g->p); @+info|=b010; @+} + if (g->m.f!=0.0) { hput_stretch(&g->m); @+info|=b001; @+} + } + else if ( g->w.h==0.0 && g->w.v==0.0 && (g->p.f==0.0 || g->m.f==0.0)) + { HPUT32(g->w.w); @+ info=b100; + if (g->p.f!=0.0) { hput_stretch(&g->p); @+info|=b010; @+} + if (g->m.f!=0.0) { hput_stretch(&g->m); @+info|=b001; @+} + } + else@/ + { hput_stretch(&g->p);@+ hput_stretch(&g->m); + hput_xdimen_node(&(g->w)); + info=b111; + } + return TAG(glue_kind,info); +} +@ + +\section{Lists}\hascode\label{lists} +When a node contains multiple other nodes, we package these nodes into +a list\index{list} node. It is important to note that list nodes +never occur as individual nodes, they only occur as parts of other +nodes. In total, we have three different types of lists: plain lists +that use the kind-value |list_kind|, text\index{text} lists that use +the kind-value |text_kind|, and parameter\index{parameter} lists that use the +kind-value |param_kind|. A description of the first two types of +lists follows here. Parameter lists are described in section~\secref{paramlist}. + +Because lists are of variable size, it is not possible in the short +format to tell from the kind and info bits of a tag byte the size of +the list node. So advancing from the beginning of a list node to the +next node after the list is not as simple as usual. To solve this +problem, we store the size of the list immediately after the start +byte and before the end byte. Alternatively we could require programs +to traverse the entire list. The latter solution is more compact but +inefficient for list with many nodes; our solution will cost some +extra bytes, but the amount of extra bytes will only grow +logarithmically with the size of the \HINT\ file. It would be +possible to allow both methods so that a \HINT\ file could balance +size and time trade-offs by making small lists---where the size can be +determined easily by reading the entire list---without size +information and making large lists with size information so that they +can be skipped easily without reading them. But the added complexity +seems too high a price to pay. + + +Now consider the problem of reading a content stream starting at an arbitrary +position $i$ in the middle of the stream. This situation occurs +naturally when resynchronizing\index{resynchronization} a content stream after +an error has been detected, but implementing links poses a similar problem. +We can inspect the byte at position $i$ and see +if it is a valid tag. If yes, we are faced with the problem of +verifying that this is not a mere coincidence. +So we determine the size $s$ of the node. If the byte in question is a start byte, +we should find a matching byte $s$ bytes later in the stream; if it is an end byte, +we should find the matching byte $s$ bytes earlier in the stream; if we +find no matching byte, this was neither a start nor an end byte. +If we find exactly one matching byte, we can be quite confident (error +probability 1/256 if assuming equal probability of all byte values) +that we have found a tag, and we know whether +it is the beginning or the end tag. If we find two matching byte, we +have most likely the start or the end of a node, but we do not know which +of the two. To find out which of the two possibilities is true +or to reduce the probability of an error, we can +check the start and end byte of the node immediately preceding a start byte or +immediately following an end byte in a similar way. +By testing two more byte, this additional check will reduce the error +probability further to $1/2^{24}$ (under the same assumption as before). So +checking more nodes is rarely necessary. This whole schema +would, however, not work if we happen to find a tag byte that indicated +either the begin or the end of a list without specifying the size +of the list. Sure, we can verify the bytes before and after it to +find out whether the byte following it is the begin of a node and the +byte preceding it is the end of a node, but we still don't know if the +byte itself starts a node list or ends a node list. Even reading along +in either direction until finding a matching tag will not answer the +question. The situation is better if we specify a +size: we can read the suspected size after or before the tag and check if we +find a matching tag and size at the position indicated. +In the short format, we use the |info| value to indicate the number of +byte used to store the list size: A list with $0<|info|\le 5$ +uses $|info|-1$ byte to store the size. +The info value zero is reserved for references to predefined lists +(which are currently not implemented). + +Storing the list size immediately preceding the end tag creates a new +problem: If we try to recover from an error, we might not know the +size of the list and searching for the end of a list, we might be +unable to tell the difference between the bytes that encode the list +size and the start tag of a possible next node. If we parse the +content backward, the problem is completely symmetric. + +To solve the problem, we insert an additional byte immediately before +the final size and after the initial size marking the size boundary. +We choose the byte values |0xFF|, |0xFE|, |0xFD|, and |0xFC| which can +not be confused with valid tag bytes and indicate that the size is +stored using 1, 2, 3, or 4 byte respectively. Under regular +circumstances, these bytes are simply skipped. When searching for the +list end (or start) these bytes would correspond to +|TAG(penalty_kind,i)| with $7 \ge \hbox{|i|} \ge 4$ and can not be +confused with valid penalty nodes which use only the info values 0, 1, +and~2. An empty list uses the info value 1 and has neither a size bytes +nor boundary bytes; it consists only of the two tags. + +We are a bit lazy when it comes to the internal representation of a list. +Since we need the representation as a short format byte sequence anyway, +it consists of the position |p| of the start of the byte sequence +combined with an integer |s| giving the size of the byte sequence. +If the list is empty, |s| is zero. + +@= +typedef struct {@+ +kind_t k; @+ +uint32_t p;@+ +uint32_t s;@+ +} list_t; +@ + +The major drawback of this choice of representation is that it ties +together the reading of the long format and the writing of the short +format; these are no longer independent. +So starting with the present section, we have to take the short format +representation of a node into account already when we parse the long +format representation. + +In the long format, we may start a list node with an +estimate\index{estimate} of the size needed to store the list in the +short format. We do not want to require the exact size because this +would make editing of long format \HINT\ files almost impossible. Of +course this makes it also impossible to derive the exact |s| value of +the internal representation from the long format +representation. Therefore we start by parsing the estimate of the list +size and use it to reserve the necessary number of byte to store the +size. Then we parse the |content_list|. As a side effect---and this +is an important point---this will write the list content in short +format into the output buffer. As mentioned above, whenever a node +contains a list, we need to consider this side effect when we give the +parsing rules. We will see examples for this in +section~\secref{composite}. + +The function |hput_list| will be called {\it after} the short format +of the list is written to the output. Before we pass the internal +representation of the list to the |hput_list| +function, we update |s| and |p|. Further, we pass the position in the stream where the +list size and its boundary mark is supposed to be. +Before |hput_list| is called, space for the tag, the size, and the boundary mark +is allocated based on the estimate. The function +|hsize_bytes| computes the number of byte required to store the list +size, and the function |hput_list_size| will later write the list +size. If the estimate turns out to be wrong, the list data can be moved +to make room for a larger or smaller size field. + + +If the long format does not specify a size estimate, a suitable default must be chosen. +A statistical analysis shows +% +%statistics about list sizes using my old prototype +% +%name type size_byte list_count total_size +%hello.hnt text 1 6 748 +% text 2 2 1967 +% list 1 65 3245 +% list 2 1 352 +%web2w.hnt text 1 1043 121925 +% text 2 1344 859070 +% list 1 19780 725514 +% list 2 487 199243 +%ctex.hnt text 1 9121 4241128 +% text 2 12329 7872687 +% text 3 1 75010 +% list 1 121557 4600743 +% list 2 222 147358 +% +that most plain lists need only a single byte to store the size; and even the +total amount of data contained in these lists exceeds the amount of data stored +in longer lists by a factor of about 3. Hence if we do not have an estimate, +we reserve only a single byte to store the size of a list. +The statistics looks different for lists stored as a text: The number of texts +that require two byte for the size is slightly larger than the number of texts that +need only one byte, and the total amount of data stored in these texts is larger +by a factor of 2 to 7 than the total amount of data found in all other texts. +Hence as a default, we reserve two byte to store the size for texts. + + +\subsection{Plain Lists}\label{plainlists} +Plain list nodes start and end with a tag of kind |list_kind|. + +Not uncommon are empty\index{empty list} lists; these are the only lists that can be +stored using $|info|=1$; such a list has zero bytes of size +information, and no boundary bytes either; implicitly its size is zero. +The |info| value 0 is not used since we do not use predefined plain lists. + +Writing the long format uses the fact that the function +|hget_content_node|, as implemented in the \.{stretch} program, will +output the node in the long format. + +\readcode +@s list symbol +@s content_list symbol +@s estimate symbol +@s position symbol + +@= +%type list +%type position content_list +@ + +@= +position: {$$=hpos-hstart;}; +content_list: @+ position @+ + | content_list content_node; +estimate: {hpos+=2; } @+ + | UNSIGNED {hpos+=hsize_bytes($1)+1; } ; +list: start estimate content_list END @/ + {@+$$.k=list_kind;@+ $$.p=$3; @+ $$.s=(hpos-hstart)-$3; + hput_tags($1,hput_list($1+1, &($$)));@+}; +@ + +\writecode +@= +void hwrite_list(list_t *l) +{ uint32_t h=hpos-hstart, e=hend-hstart; /* save |hpos| and |hend| */ + hpos=l->p+hstart;@+ hend=hpos+l->s; + if (l->k==list_kind ) @@; + else if (l->k==text_kind) @@; + else QUIT("List expected got %s", content_name[l->k]); + hpos=hstart+h;@+ hend=hstart+e; /* restore |hpos| and |hend| */ +} +@ + +@= +{@+if (l->s==0) hwritef(" <>");@/ + else@/ + {@+DBG(DBGNODE,"Write list at 0x%x size=%u\n", l->p, l->s); + @+hwrite_start();@+ + if (section_no==2) hwrite_label(); + if (l->s>0xFF) hwritef("%d",l->s); + while(hpos= +void hget_size_boundary(info_t info) +{ uint32_t n; + if (info<2) return; + n=HGET8; + if (n-1!=0x100-info) QUIT(@["Size boundary byte 0x%x with info value %d at " SIZE_F@], + n, info,hpos-hstart-1); +} + +uint32_t hget_list_size(info_t info) +{ uint32_t n=0; + if (info==1) return 0; + else if (info==2) n=HGET8; + else if (info==3) HGET16(n); + else if (info==4) HGET24(n); + else if (info==5) HGET32(n); + else QUIT("List info %d must be 1, 2, 3, 4, or 5",info); + return n; +} + +void hget_list(list_t *l) +{@+if (KIND(*hpos)!=list_kind && @/ + KIND(*hpos)!=text_kind &&@| KIND(*hpos)!=param_kind) @/ + QUIT("List expected at 0x%x", (uint32_t)(hpos-hstart)); + else + { + @@; + l->k=KIND(a); + HGET_LIST(INFO(a),*l); + @@; + DBG(DBGNODE,"Get list at 0x%x size=%u\n", l->p, l->s); + } +} +@ + +@= +#define @[HGET_LIST(I,L)@] \ + (L).s=hget_list_size(I); hget_size_boundary(I);\ + (L).p=hpos-hstart; \ + hpos=hpos+(L).s; hget_size_boundary(I);\ + { uint32_t s=hget_list_size(I); \ + if (s!=(L).s) \ + QUIT(@["List sizes at 0x%x and " SIZE_F " do not match 0x%x != 0x%x"@],node_pos+1,hpos-hstart-I-1,(L).s,s);} +@ + +\putcode + +@= +uint8_t hsize_bytes(uint32_t n) +{ @+if (n==0) return 0; + else if (n<0x100) return 1; + else if (n<0x10000) return 2; + else if (n<0x1000000) return 3; + else return 4; +} + +void hput_list_size(uint32_t n, int i) +{ @+if (i==0) ; + else if (i==1) HPUT8(n); + else if (i==2) HPUT16(n); + else if (i==3) HPUT24(n); + else HPUT32(n); +} + +uint8_t hput_list(uint32_t start_pos, list_t *l) +{ @+if (l->s==0) + { hpos=hstart+start_pos; return TAG(l->k,1);@+} + else@/ + { uint32_t list_end=hpos-hstart; + int i=l->p -start_pos-1; /* number of byte allocated for size */ + int j=hsize_bytes(l->s); /* number of byte needed for size */ + DBG(DBGNODE,"Put list at 0x%x size=%u\n", l->p, l->s); + if (i>j && l->s> 0x100) j=i; /* avoid moving large lists */ + if (i!=j)@/ + { int d= j-i; + DBG(DBGNODE,"Moving %u byte by %d\n", l->s,d); + if (d>0) HPUTX(d); + memmove(hstart+l->p+d,hstart+l->p,l->s); + @@; + l->p=l->p+d;@+ + list_end=list_end+d; + } + hpos=hstart+start_pos; @+ hput_list_size(l->s,j);@+ HPUT8(0x100-j); + hpos=hstart+list_end;@+ HPUT8(0x100-j);@+ hput_list_size(l->s,j); + return TAG(l->k,j+1); + } +} + +@ + + + +\subsection{Texts}\label{text} +A Text\index{text} is a list of nodes with a representation optimized +for character nodes. In the long format, a sequence of characters +like ``{\tt Hello}'' is written ``\.{} \.{} \.{} \.{} \.{}'', and +even in the short format it requires 4 byte per character! As a text, +the same sequence is written ``{\tt\,"Hello"\,}'' in the long format and the +short format requires usually just 1 byte per character. Indeed +except the bytes with values from |0x00| to |0x20|, which are +considered control\index{control code} codes, all bytes and all +\hbox{UTF-8}\index{UTF8} multibyte sequences are simply considered +character\index{character code} codes. They are equivalent to a glyph +node in the ``current font''. The current\index{current font} +font\index{font} is font number 0 at the beginning of a text and it +can be changed using the control codes. We introduce the concept of a +``current font'' because we do not expect the font to change too +often, and it allows for a more compact representation if we do not +store the font with every character code. It has an important +disadvantage though: storing only font changes prevents us from +parsing a text backwards; we always have to start at the beginning of +the text, where the font is known to be font number~0. + +Defining a second format for encoding lists of nodes adds another +difficulty to the problem we had discussed at the beginning of +section~\secref{lists}. When we try to recover from an error and start +reading a content stream at an arbitrary position, the first thing we +need to find out is whether at this position we have the tag byte of +an ordinary node or whether we have a position inside a text. + +Inside a text, character nodes start with a byte in the range +|0x21|--|0xF7|. This is a wide range and it overlaps considerably with +the range of valid tag bytes. It is however possible to choose the +kind-values in such a way that the control codes do not overlap with +the valid tag bytes that start a node. For this reason, the values +|text_kind==0|, |list_kind==1|, |param_kind==2|, |xdimen_kind==3|, and +|adjust_kind==4| were chosen on page~\pageref{kinddef}. Texts, lists, +parameter lists, and extended dimensions occur only {\it inside} of +content nodes, but are not content nodes in their own right; so the +values |0x00| to |0x1F| are not used as tag bytes of content +nodes. The value |0x20| would, as a tag byte, indicate an adjust node +(|adjust_kind==4|) with info value zero. Because there are no +predefined adjustments, |0x20| is not used as a tag byte either. +(An alternative choice would be to use the kind value 4 for paragraph +nodes because there are no predefined paragraphs.) + +The largest byte that starts an UTF8 code is |0xF7|; hence, there are +eight possible control codes, from |0xF8| to |0xFF|, available. The +first three values |0xF8|, |0xF9|, and |0xFA| are actually used for +penalty nodes with info values, 0, 1, and 2. The last four |0xFC|, +|0xFD|, |0xFE|, and |0xFF| are used as boundary marks for the text +size and therefore we use only |0xFB| as control code. + +In the long format, we do not provide a syntax for specifying a size +estimate\index{estimate} as we did for plain lists, because we expect +text to be quite short. We allocate two byte for the size and hope +that this will prove to be sufficient most of the time. Further, we +will disallow the use of non-printable ASCII codes, because these +are---by definition---not very readable, and we will give special +meaning to some of the printable ASCII codes because we will need a +notation for the beginning and ending of a text, for nodes inside a +text, and the control codes. + +Here are the details: +\itemize + +\item In the long format, a text starts and ends with a +double\index{double quote} quote character ``{\tt "}''. In the short +format, texts are encoded similar to lists using the kind-value +|text_kind|. + +\item Arbitrary nodes can be embedded inside a text. In the long +format, they are enclosed in pointed brackets \.{<} \dots \.{>} as +usual. In the short format, an arbitrary node can follow the control +code $|txt_node|=|0x1E|$. Because text may occur in nodes, the scanner +needs to be able to parse texts nested inside nodes nested inside +nodes nested inside texts \dots\ To accomplish this, we use the +``stack'' option of \.{flex} and include the pushing and popping of the +stack in the macros |SCAN_START| and |SCAN_END|. + +\item The space\index{space character} character ``\.{\ }'' with ASCII +value |0x20| stands in both formats for the font specific interword +glue node (control code |txt_glue|). + +\item The hyphen\index{hyphen character} character ``\.{-}'' in the +long format and the control code $|txt_hyphen|=|0x1F|$ in the short +format stand for the font specific discretionary hyphenation node. + +\item In the long format, the backslash\index{backslash} character +``\.{\\}'' is used as an escape character. It is used to introduce +notations for control codes, as described below, and to access the +character codes of those ASCII characters that otherwise carry a +special meaning. For example ``{\tt \BS "}'' denotes the character code +of the double quote character ``{\tt "}''; and similarly ``\.{\\\\}'', +``\.{\\<}'', ``\.{\\>}'', ``\.{\\\ }'', and ``\.{\\-}'' denote the +character codes of ``\.{\\}'', ``\.{<}'', ``\.{>}'', ``\.{\ }'', and +``\.{-}'' respectively. + + +\item In the long format, a TAB-character (ASCII code +|0x09|)\index{tab character} is silently converted to a +space\index{space character} character (ASCII code |0x20|); +a NL-character\index{newline character} (ASCII code |0x0A|), together +with surrounding spaces, TAB-characters, +and CR-characters\index{carriage return character} (ASCII code |0x0D|), +is silently converted to a single space character. All other ASCII +characters in the range |0x00| to |0x1F| are not allowed inside a +text. This rule avoids the problems arising from ``invisible'' +characters embedded in a text and it allows to break texts into lines, +even with indentation\index{indentation}, at word boundaries. + +To allow breaking a text into lines without inserting spaces, a +NL-character together with surrounding spaces, TAB-characters, and +CR-characters is completely ignored if the whole group of spaces, +TAB-characters, CR-characters, and the NL-character is preceded by a +backslash character. + +For example, the text ``\.{"There\ is\ no\ more\ gas\ in\ the\ +tank."}''\hfil\break can be written as \medskip + +\qquad\vbox{\hsize=0.5\hsize\noindent +\.{"There\ is\ }\hfil\break +\.{\hbox to 2em {$\rightarrow$\hfill}no more g\\\ \ }\hfil\break +\.{\hbox to 2em {$\rightarrow$\hfill}as in the tank."} +}\hss + +To break long lines when writing a long format file, we use the +variable |txt_length| to keep track of the approximate length of the +current line. + +\item The control codes $|txt_font|=|0x00|$, |0x01|, |0x02|, \dots, +and |0x07| are used to change the current font to font +number 0, 1, 2, \dots, and 7. In the long format these control +codes are written \.{\\0}, \.{\\1}, \.{\\2}, \dots, and \.{\\7}. + +\item The control code $|txt_global|=|0x08|$ is followed by a second +parameter byte. If the value of the parameter byte is $n$, it will set +the current font to font number $n$. In the long format, the two byte +sequence is written ``\.{\\F}$n$\.{\\}'' where $n$ is the decimal +representation of the font number. + + +\item The control codes |0x09|, |0x0A|, |0x0B|, |0x0C|, |0x0E|, +|0x0E|, |0x0F|, and |0x10| are also followed by a second parameter +byte. They are used to reference the global definitions of +penalty\index{penalty}, kern\index{kern}, ligature\index{ligature}, +disc\index{discretionary hyphen}, glue\index{glue}, language\index{language}, +rule\index{rule}, and image\index{image} nodes. The parameter byte +contains the reference number. For example, the byte sequence |0x09| +|0x03| is equivalent to the node \.{}. +In the long format these two-byte sequences are written, +``\.{\\P}$n$\.{\\}'' (penalty), +``\.{\\K}$n$\.{\\}'' (kern), +``\.{\\L}$n$\.{\\}'' (ligature), +``\.{\\D}$n$\.{\\}'' (disc), +``\.{\\G}$n$\.{\\}'' (glue), +``\.{\\S}$n$\.{\\}'' (speak or German ``Sprache''), +``\.{\\R}$n$\.{\\}'' (rule), and +``\.{\\I}$n$\.{\\}'' (image), where $n$ is the decimal representation + of the parameter value. + + +\item The control codes from $|txt_local|=|0x11|$ to |0x1C| are used +to reference one of the 12 font specific parameters\index{font +parameter}. In the long format they are written ``\.{\\a}'', +``\.{\\b}'', ``\.{\\c}'', \dots, ``\.{\\j}'', ``\.{\\k}'',``\.{\\l}''. + + +\item The control code $|txt_cc|=|0x1D|$ is used as a prefix for an +arbitrary character code represented as an UTF-8 multibyte sequence. +Its main purpose is providing a method for including character codes +less or equal to |0x20| which otherwise would be considered control +codes. In the long format, the byte sequence is written ``\.{\\C}$n$\.{\\}'' +where $n$ is the decimal representation of the character code. + + +\item The control code $|txt_node|=|0x1E|$ is used as a prefix for an +arbitrary node in short format. In the long format, it is written +``\.{<}'' and is followed by the node content in long format +terminated by ``\.{>}''. + +\item The control code $|txt_hyphen|=|0x1F|$ is used to access the +font specific discretionary hyphen\index{hyphen}. In the long format +it is simply written as ``\.{-}''. + +\item The control code $|txt_glue|=|0x20|$ is the space character, it +is used to access the font specific interword\index{interword glue} +glue. In the long format, we use the space character\index{space +character} ``\.{\ }'' as well. + +\item The control code $|txt_ignore|=|0xFB|$ is ignored, its position +can be used in a link to specify a position between two characters. In +the long format it is written as ``\.{\\@@}''. + +\enditemize +For the control codes, we define an enumeration type +and for references, a reference type. +@= +typedef enum { @+txt_font=0x00, txt_global=0x08, txt_local=0x11, + txt_cc=0x1D, txt_node=0x1E, txt_hyphen=0x1F, + txt_glue=0x20, txt_ignore=0xFB} txt_t; +@ + +\readcode +@s TXT symbol +@s TXT_START symbol +@s TXT_END symbol +@s TXT_FONT symbol +@s TXT_LOCAL symbol +@s TXT_GLOBAL symbol +@s TXT_FONT_GLUE symbol +@s TXT_FONT_HYPHEN symbol +@s TXT_CC symbol +@s TXT_IGNORE symbol +@s text symbol +@= +%x TXT +@ + +@= +%token TXT_START TXT_END TXT_IGNORE +%token TXT_FONT_GLUE TXT_FONT_HYPHEN +%token TXT_FONT TXT_LOCAL +%token TXT_GLOBAL +%token TXT_CC +%type text +@ + +@= +::@=\"@> :< SCAN_TXT_START; return TXT_START; >: + +{ +::@=\"@> :< SCAN_TXT_END; return TXT_END; >: + +::@="<"@> :< SCAN_START; return START; >: +::@=">"@> :< QUIT("> not allowed in text mode");>: + +::@=\\\\@> :< yylval.u='\\'; return TXT_CC; >: +::@=\\\"@> :< yylval.u='"'; return TXT_CC; >: +::@=\\"<"@> :< yylval.u='<'; return TXT_CC; >: +::@=\\">"@> :< yylval.u='>'; return TXT_CC; >: +::@=\\" "@> :< yylval.u=' '; return TXT_CC; >: +::@=\\"-"@> :< yylval.u='-'; return TXT_CC; >: +::@=\\"@@"@> :< return TXT_IGNORE; >: + +::@=[ \t\r]*(\n[ \t\r]*)+@> :< return TXT_FONT_GLUE; >: +::@=\\[ \t\r]*\n[ \t\r]*@> :< ; >: + +::@=\\[0-7]@> :< yylval.u=yytext[1]-'0'; return TXT_FONT; >: + +::@=\\F[0-9]+\\@> :< SCAN_REF(font_kind); return TXT_GLOBAL; >: +::@=\\P[0-9]+\\@> :< SCAN_REF(penalty_kind); return TXT_GLOBAL; >: +::@=\\K[0-9]+\\@> :< SCAN_REF(kern_kind); return TXT_GLOBAL; >: +::@=\\L[0-9]+\\@> :< SCAN_REF(ligature_kind); return TXT_GLOBAL; >: +::@=\\D[0-9]+\\@> :< SCAN_REF(disc_kind); return TXT_GLOBAL; >: +::@=\\G[0-9]+\\@> :< SCAN_REF(glue_kind); return TXT_GLOBAL; >: +::@=\\S[0-9]+\\@> :< SCAN_REF(language_kind); return TXT_GLOBAL; >: +::@=\\R[0-9]+\\@> :< SCAN_REF(rule_kind); return TXT_GLOBAL; >: +::@=\\I[0-9]+\\@> :< SCAN_REF(image_kind); return TXT_GLOBAL; >: + + +::@=\\C[0-9]+\\@> :< SCAN_UDEC(yytext+2); return TXT_CC; >: + +::@=\\[a-l]@> :< yylval.u=yytext[1]-'a'; return TXT_LOCAL; >: +::@=" "@> :< return TXT_FONT_GLUE; >: +::@="-"@> :< return TXT_FONT_HYPHEN; >: + +::@={UTF8_1}@> :< SCAN_UTF8_1(yytext); return TXT_CC; >: +::@={UTF8_2}@> :< SCAN_UTF8_2(yytext); return TXT_CC; >: +::@={UTF8_3}@> :< SCAN_UTF8_3(yytext); return TXT_CC; >: +::@={UTF8_4}@> :< SCAN_UTF8_4(yytext); return TXT_CC; >: +} +@ + +@= +#define @[SCAN_REF(K)@] @[yylval.rf.k=K;@+ yylval.rf.n=atoi(yytext+2)@;@] +static int scan_level=0; +#define SCAN_START @[yy_push_state(INITIAL);@+if (1==scan_level++) hpos0=hpos;@] +#define SCAN_END @[if (scan_level--) yy_pop_state(); @/else QUIT("Too many '>' in line %d",yylineno)@] +#define SCAN_TXT_START @[BEGIN(TXT)@;@] +#define SCAN_TXT_END @[BEGIN(INITIAL)@;@] +@ +@s txt symbol + +@= +list: TXT_START position @| + {hpos+=4; /* start byte, two size byte, and boundary byte */ } + text TXT_END@| + { $$.k=text_kind;$$.p=$4; $$.s=(hpos-hstart)-$4; + hput_tags($2,hput_list($2+1, &($$)));@+}; +text: position @+| text txt; + +txt: TXT_CC { hput_txt_cc($1); } + | TXT_FONT { REF(font_kind,$1); hput_txt_font($1); } + | TXT_GLOBAL { REF($1.k,$1.n); hput_txt_global(&($1)); } + | TXT_LOCAL { RNG("Font parameter",$1,0,11); hput_txt_local($1); } + | TXT_FONT_GLUE { HPUTX(1); HPUT8(txt_glue); } + | TXT_FONT_HYPHEN { HPUTX(1);HPUT8(txt_hyphen); } + | TXT_IGNORE { HPUTX(1);HPUT8(txt_ignore); } + | { HPUTX(1); HPUT8(txt_node);} content_node; +@ + +The following function keeps track of the position in the current line. +It the line gets too long it will break the text at the next space +character. If no suitable space character comes along, +the line will be broken after any regular character. + +\writecode +@= +{@+if (l->s==0) hwritef(" \"\""); + else@/ + { int pos=nesting+20; /* estimate */ + hwritef(" \""); + while(hpos=100)@/ + { hwritec('\\'); @+hwrite_nesting(); @+pos=nesting; @+} + else + pos+=i; + } + hwritec('"'); + } +} +@ + + +The function returns the number of characters written +because this information is needed in |hget_txt| below. + +@= +int hwrite_txt_cc(uint32_t c) +{@+ if (c<0x20) + return hwritef("\\C%d\\",c); + else@+ + switch(c) + { case '\\': return hwritef("\\\\"); + case '"': return hwritef("\\\""); + case '<': return hwritef("\\<"); + case '>': return hwritef("\\>"); + case ' ': return hwritef("\\ "); + case '-': return hwritef("\\-"); + default: return option_utf8?hwrite_utf8(c):hwritef("\\C%d\\",c); + } +} +@ + +\getcode +@= +#define @[HGET_GREF(K,S)@] {uint8_t n=HGET8;@+ REF(K,n); @+ return hwritef("\\" S "%d\\",n);@+} + +@ + +The function |hget_txt| reads a text element and writes it immediately. +To enable the insertion of line breaks when writing a text, we need to keep track +of the number of characters in the current line. For this purpose +the function |hget_txt| returns the number of characters written. +It returns $-1$ if a space character needs to be written +providing a good opportunity for a break. + +@= +int hget_txt(void) +{@+ if (*hpos>=0x80 && *hpos<=0xF7) + { if (option_utf8) + return hwrite_utf8(hget_utf8()); + else + return hwritef("\\C%d\\",hget_utf8()); + } + else @/ + { uint8_t a; + a=HGET8; + switch (a) + { case txt_font+0: return hwritef("\\0"); + case txt_font+1: return hwritef("\\1"); + case txt_font+2: return hwritef("\\2"); + case txt_font+3: return hwritef("\\3"); + case txt_font+4: return hwritef("\\4"); + case txt_font+5: return hwritef("\\5"); + case txt_font+6: return hwritef("\\6"); + case txt_font+7: return hwritef("\\7"); + case txt_global+0: HGET_GREF(font_kind,"F"); + case txt_global+1: HGET_GREF(penalty_kind,"P"); + case txt_global+2: HGET_GREF(kern_kind,"K"); + case txt_global+3: HGET_GREF(ligature_kind,"L"); + case txt_global+4: HGET_GREF(disc_kind,"D"); + case txt_global+5: HGET_GREF(glue_kind,"G"); + case txt_global+6: HGET_GREF(language_kind,"S"); + case txt_global+7: HGET_GREF(rule_kind,"R"); + case txt_global+8: HGET_GREF(image_kind,"I"); + case txt_local+0: return hwritef("\\a"); + case txt_local+1: return hwritef("\\b"); + case txt_local+2: return hwritef("\\c"); + case txt_local+3: return hwritef("\\d"); + case txt_local+4: return hwritef("\\e"); + case txt_local+5: return hwritef("\\f"); + case txt_local+6: return hwritef("\\g"); + case txt_local+7: return hwritef("\\h"); + case txt_local+8: return hwritef("\\i"); + case txt_local+9: return hwritef("\\j"); + case txt_local+10: return hwritef("\\k"); + case txt_local+11: return hwritef("\\l"); + case txt_cc: return hwrite_txt_cc(hget_utf8()); + case txt_node: { int i; + @@; + i=hwritef("<"); + i+= hwritef("%s",content_name[KIND(a)]);@+ hget_content(a); + @@; + hwritec('>');@+ return i+10; /* just an estimate */ + } + case txt_hyphen: hwritec('-'); @+return 1; + case txt_glue: return -1; + case '<': return hwritef("\\<"); + case '>': return hwritef("\\>"); + case '"': return hwritef("\\\""); + case '-': return hwritef("\\-"); + case txt_ignore: return hwritef("\\@@"); + default: hwritec(a); @+return 1; + } + } +} + +@ + + +\putcode +@= + +void hput_txt_cc(uint32_t c) +{ @+ if (c<=0x20) { HPUTX(2); HPUT8(txt_cc);@+ HPUT8(c); @+ } + else hput_utf8(c); +} + +void hput_txt_font(uint8_t f) +{@+ if (f<8) HPUTX(1),HPUT8(txt_font+f); + else QUIT("Use \\F%d\\ instead of \\%d for font %d in a text",f,f,f); +} + +void hput_txt_global(ref_t *d) +{ @+ HPUTX(2); + switch (d->k) + { case font_kind: HPUT8(txt_global+0);@+ break; + case penalty_kind: HPUT8(txt_global+1);@+ break; + case kern_kind: HPUT8(txt_global+2);@+ break; + case ligature_kind: HPUT8(txt_global+3);@+ break; + case disc_kind: HPUT8(txt_global+4);@+ break; + case glue_kind: HPUT8(txt_global+5);@+ break; + case language_kind: HPUT8(txt_global+6);@+ break; + case rule_kind: HPUT8(txt_global+7);@+ break; + case image_kind: HPUT8(txt_global+8);@+ break; + default: QUIT("Kind %s not allowed as a global reference in a text",NAME(d->k)); + } + HPUT8(d->n); +} + +void hput_txt_local(uint8_t n) +{ HPUTX(1); + HPUT8(txt_local+n); +} +@ + + +@= +typedef struct { @+kind_t k; @+int n; @+} ref_t; +@ + + +\section{Composite Nodes}\hascode +\label{composite} +The nodes that we consider in this section can contain one or more list nodes. +When we implement the parsing\index{parsing} routines +for composite nodes in the long format, we have to take into account +that parsing such a list node will already write the list node +to the output. So we split the parsing of composite nodes into several parts +and store the parts immediately after parsing them. On the parse stack, we will only +keep track of the info value. +This new strategy is not as transparent as our previous strategy used for +simple nodes where we had a clean separation of reading and writing: +reading would store the internal representation of a node and writing the internal +representation to output would start only after reading is completed. +The new strategy, however, makes it easier to reuse +the grammar\index{grammar} rules for the component nodes. + +Another rule applies to composite nodes: in the short format, the subnodes +will come at the end of the node, and especially a list node that contains content nodes +comes last. This helps when traversing the content section as we will see in +appendix~\secref{fastforward}. + +\subsection{Boxes}\label{boxnodes} +The central structuring elements of \TeX\ are boxes\index{box}. +Boxes have a height |h|, a depth |d|, and a width |w|. +The shift amount |a| shifts the contents of the box, the glue ratio\index{glue ratio} |r| is a factor +applied to the glue inside the box, the glue order |o| is its order of stretchability\index{stretchability}, +and the glue sign |s| is $-1$ for shrinking\index{shrinkability}, 0 for rigid, and $+1$ for stretching. +Most importantly, a box contains a list |l| of content nodes inside the box. + + +@= +typedef struct @/{@+ dimen_t h,d,w,a;@+ float32_t r;@+ int8_t s,o; @+list_t l; @+} box_t; +@ + +There are two types of boxes: horizontal\index{horizontal box} boxes +and vertical\index{vertical box} boxes. +The difference between the two is simple: +a horizontal box aligns the reference\index{reference point} +points of its content nodes horizontally, and a positive shift amount\index{shift amount} |a| +shifts the box down; +a vertical box aligns\index{alignment} the reference\index{reference point} +points vertically, and a positive shift amount |a| shifts the box right. + +Not all box parameters are used frequently. In the short format, we use the info bits +to indicated which of the parameters are used. +Where as the width of a horizontal box is most of the time (80\%) nonzero, +other parameters are most of the time zero, +like the shift amount (99\%) or the glue settings (99.8\%). +The depth is zero in about 77\%, the height in about 53\%, +and both together are zero in about 47\%. The results for vertical boxes, +which constitute about 20\% of all boxes, are similar, +except that the depth is zero in about 89\%, +but the height and width are almost never zero. +For this reason we use bit |b001| to indicate a nonzero depth, +bit |b010| for a nonzero shift amount, and |b100| for nonzero glue settings. +Glue sign and glue order can be packed as two nibbles in a single byte. +% A different use of the info bits for vertical and horizontal boxes is possible, +% but does not warrant the added complexity. + + + +\goodbreak +\readcode +@s HBOX symbol +@s VBOX symbol +@s box symbol +@s boxparams symbol +@s hbox_node symbol +@s vbox_node symbol +@s box_dimen symbol +@s box_shift symbol +@s box_glue_set symbol +@= +%token HBOX "hbox" +%token VBOX "vbox" +%token SHIFTED "shifted" +%type box box_dimen box_shift box_glue_set + +@ +@= +::@=hbox@> :< return HBOX; >: +::@=vbox@> :< return VBOX; >: +::@=shifted@> :< return SHIFTED; >: +@ + +@=@/ + +box_dimen: dimension dimension dimension @/ + {$$= hput_box_dimen($1,$2,$3); }; +box_shift: {$$=b000;} @+ + | SHIFTED dimension {$$=hput_box_shift($2);}; + +box_glue_set: {$$=b000;} + | PLUS stretch { $$=hput_box_glue_set(+1,$2.f,$2.o); } + | MINUS stretch { $$=hput_box_glue_set(-1,$2.f,$2.o); }; + + +box: box_dimen box_shift box_glue_set list {$$=$1|$2|$3; }; + +hbox_node: start HBOX box END { hput_tags($1, TAG(hbox_kind,$3)); }; +vbox_node: start VBOX box END { hput_tags($1, TAG(vbox_kind,$3)); }; +content_node: hbox_node @+ | vbox_node; +@ + +\writecode +@= +void hwrite_box(box_t *b) +{ hwrite_dimension(b->h); + hwrite_dimension(b->d); + hwrite_dimension(b->w); + if (b->a!=0) { hwritef(" shifted"); @+hwrite_dimension(b->a); @+} + if (b->r!=0.0 && b->s!=0 )@/ + { @+if (b->s>0) @+hwritef(" plus"); @+else @+hwritef(" minus"); + @+hwrite_float64(b->r); @+hwrite_order(b->o); + } + hwrite_list(&(b->l)); +} +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(hbox_kind,b000): {box_t b; @+HGET_BOX(b000,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b001): {box_t b; @+HGET_BOX(b001,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b010): {box_t b; @+HGET_BOX(b010,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b011): {box_t b; @+HGET_BOX(b011,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b100): {box_t b; @+HGET_BOX(b100,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b101): {box_t b; @+HGET_BOX(b101,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b110): {box_t b; @+HGET_BOX(b110,b); @+hwrite_box(&b);@+} @+ break; +case TAG(hbox_kind,b111): {box_t b; @+HGET_BOX(b111,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b000): {box_t b; @+HGET_BOX(b000,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b001): {box_t b; @+HGET_BOX(b001,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b010): {box_t b; @+HGET_BOX(b010,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b011): {box_t b; @+HGET_BOX(b011,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b100): {box_t b; @+HGET_BOX(b100,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b101): {box_t b; @+HGET_BOX(b101,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b110): {box_t b; @+HGET_BOX(b110,b); @+hwrite_box(&b);@+} @+ break; +case TAG(vbox_kind,b111): {box_t b; @+HGET_BOX(b111,b); @+hwrite_box(&b);@+} @+ break; +@ + +@= +#define @[HGET_BOX(I,B)@] \ +HGET32(B.h);\ +if ((I)&b001) HGET32(B.d); @+ else B.d=0;\ +HGET32(B.w);\ +if ((I)&b010) HGET32(B.a); @+else B.a=0;\ +if ((I)&b100) @/{ B.r=hget_float32();@+ B.s=HGET8; @+ B.o=B.s&0xF; @+B.s=B.s>>4;@+ }\ +else { B.r=0.0;@+ B.o=B.s=0;@+ }\ +hget_list(&(B.l)); +@ + +@= +void hget_hbox_node(void) +{ box_t b; + @@; + if (KIND(a)!=hbox_kind) QUIT("Hbox expected at 0x%x got %s",node_pos,NAME(a)); + HGET_BOX(INFO(a),b);@/ + @@; + hwrite_start();@+ + hwritef("hbox");@+ + hwrite_box(&b);@+ + hwrite_end(); +} + + +void hget_vbox_node(void) +{ box_t b; + @@; + if (KIND(a)!=vbox_kind) QUIT("Vbox expected at 0x%x got %s",node_pos,NAME(a)); + HGET_BOX(INFO(a),b);@/ + @@; + hwrite_start();@+ + hwritef("vbox");@+ + hwrite_box(&b);@+ + hwrite_end(); +} +@ + +\putcode +@= + +info_t hput_box_dimen(dimen_t h, dimen_t d, dimen_t w) +{ info_t i; + @+HPUT32(h); + if (d!=0) { HPUT32(d); @+i=b001;@+ } @+else@+ i=b000; + HPUT32(w); + return i; +} +info_t hput_box_shift(dimen_t a) +{ @+if (a!=0) { @+ HPUT32(a); @+return @+ b010;@+} @+ else @+return b000; +} + +info_t hput_box_glue_set(int8_t s, float32_t r, order_t o) +{ @+if (r!=0.0 && s!=0 ) + { hput_float32(r);@+ + HPUT8((s<<4)|o);@+ + return b100;@+ + } + else return b000; +} + +@ + +\subsection{Extended Boxes} +Hi\TeX\ produces two kinds of extended\index{extended box} horizontal +boxes, |hpack_kind| and |hset_kind|, and the same for vertical boxes +using |vpack_kind| and |vset_kind|. Let us focus on horizontal boxes; +the handling of vertical boxes is completely parallel. + +The \\{hpack} procedure of Hi\TeX\ produces an extended box of |hset_kind| +either if it is given an extended\index{extended dimension} dimension as its width +or if it discovers that the width of its content is an extended +dimension. After the final width of the box has been computed in the +viewer, it just remains to set the glue; a very simple operation +indeed. + +If the \\{hpack} procedure of Hi\TeX\ can not determine the natural +dimensions of the box content because it contains +paragraphs\index{paragraph} or other extended boxes, it produces a box +of |hpack_kind|. Now the viewer needs to traverse the list of content +nodes to determine the natural\index{natural dimension} +dimensions. Even the amount of stretchability\index{stretchability} +and shrinkability\index{shrinkability} has to be determined in the +viewer. For example, the final stretchability of a paragraph with some +stretchability in the baseline\index{baseline skip} skip will depend +on the number of lines which, in turn, depends on \.{hsize}. It is +not possible to merge these traversals of the box content with the +traversal necessary when displaying the box. The latter needs to +convert glue nodes into positioning instructions which requires a +fixed glue\index{glue ratio} ratio. The computation of the glue ratio, +however, requires a complete traversal of the content. + +In the short format of a box node of type |hset_kind|, |vset_kind|, +|hpack_kind|, or |vpack_kind|, the info bit |b100| indicates, if set, +a complete extended dimension, and if unset, a reference to a +predefined extended dimension for the target size; the info bit |b010| +indicates a nonzero shift amount. For a box of type |hset_kind| or +|vset_kind|, the info bit |b001| indicates, if set, a nonzero depth. +For a box of type |hpack_kind| or |vpack_kind|, the info bit |b001| +indicates, if set, an additional target size, and if unset, an exact +target size. For a box of type |vpack_kind| also the maximum depth is +given. + +\readcode +@s xbox symbol +@s hpack symbol +@s vpack symbol +@s box_goal symbol +@s HPACK symbol +@s HSET symbol +@s VPACK symbol +@s VSET symbol +@s TO symbol +@s ADD symbol +@s box_flex symbol +@s vxbox_node symbol +@s hxbox_node symbol +@s DEPTH symbol + +@= +%token HPACK "hpack" +%token HSET "hset" +%token VPACK "vpack" +%token VSET "vset" +%token DEPTH "depth" +%token ADD "add" +%token TO "to" +%type xbox box_goal hpack vpack +@ + +@= +::@=hpack@> :< return HPACK; >: +::@=hset@> :< return HSET; >: +::@=vpack@> :< return VPACK; >: +::@=vset@> :< return VSET; >: +::@=add@> :< return ADD; >: +::@=to@> :< return TO; >: +::@=depth@> :< return DEPTH; >: +@ + +@= +box_flex: plus minus { hput_stretch(&($1));hput_stretch(&($2)); }; +xbox: box_dimen box_shift box_flex xdimen_ref list {$$=$1|$2;} + | box_dimen box_shift box_flex xdimen_node list {$$=$1|$2|b100;}; + +box_goal: TO xdimen_ref {$$=b000;} + | ADD xdimen_ref {$$=b001;} + | TO xdimen_node {$$=b100;} + | ADD xdimen_node {$$=b101;}; + +hpack: box_shift box_goal list {$$=$2;}; +vpack: box_shift MAX DEPTH dimension {HPUT32($4);} @/ box_goal list {$$= $1|$6;}; + +vxbox_node: start VSET xbox END { hput_tags($1, TAG(vset_kind,$3)); } + | start VPACK vpack END { hput_tags($1, TAG(vpack_kind,$3)); }; + + +hxbox_node: start HSET xbox END { hput_tags($1, TAG(hset_kind,$3)); } + | start HPACK hpack END { hput_tags($1, TAG(hpack_kind,$3)); }; + +content_node: vxbox_node | hxbox_node; + @ + +\getcode +@= +@t\1\kern1em@> +case TAG(hset_kind,b000): HGET_SET(b000); @+ break; +case TAG(hset_kind,b001): HGET_SET(b001); @+ break; +case TAG(hset_kind,b010): HGET_SET(b010); @+ break; +case TAG(hset_kind,b011): HGET_SET(b011); @+ break; +case TAG(hset_kind,b100): HGET_SET(b100); @+ break; +case TAG(hset_kind,b101): HGET_SET(b101); @+ break; +case TAG(hset_kind,b110): HGET_SET(b110); @+ break; +case TAG(hset_kind,b111): HGET_SET(b111); @+ break;@# + +case TAG(vset_kind,b000): HGET_SET(b000); @+ break; +case TAG(vset_kind,b001): HGET_SET(b001); @+ break; +case TAG(vset_kind,b010): HGET_SET(b010); @+ break; +case TAG(vset_kind,b011): HGET_SET(b011); @+ break; +case TAG(vset_kind,b100): HGET_SET(b100); @+ break; +case TAG(vset_kind,b101): HGET_SET(b101); @+ break; +case TAG(vset_kind,b110): HGET_SET(b110); @+ break; +case TAG(vset_kind,b111): HGET_SET(b111); @+ break;@# + +case TAG(hpack_kind,b000): HGET_PACK(hpack_kind,b000); @+ break; +case TAG(hpack_kind,b001): HGET_PACK(hpack_kind,b001); @+ break; +case TAG(hpack_kind,b010): HGET_PACK(hpack_kind,b010); @+ break; +case TAG(hpack_kind,b011): HGET_PACK(hpack_kind,b011); @+ break; +case TAG(hpack_kind,b100): HGET_PACK(hpack_kind,b100); @+ break; +case TAG(hpack_kind,b101): HGET_PACK(hpack_kind,b101); @+ break; +case TAG(hpack_kind,b110): HGET_PACK(hpack_kind,b110); @+ break; +case TAG(hpack_kind,b111): HGET_PACK(hpack_kind,b111); @+ break;@# + +case TAG(vpack_kind,b000): HGET_PACK(vpack_kind,b000); @+ break; +case TAG(vpack_kind,b001): HGET_PACK(vpack_kind,b001); @+ break; +case TAG(vpack_kind,b010): HGET_PACK(vpack_kind,b010); @+ break; +case TAG(vpack_kind,b011): HGET_PACK(vpack_kind,b011); @+ break; +case TAG(vpack_kind,b100): HGET_PACK(vpack_kind,b100); @+ break; +case TAG(vpack_kind,b101): HGET_PACK(vpack_kind,b101); @+ break; +case TAG(vpack_kind,b110): HGET_PACK(vpack_kind,b110); @+ break; +case TAG(vpack_kind,b111): HGET_PACK(vpack_kind,b111); @+ break; +@ + + +@= +#define @[HGET_SET(I)@] @/\ + { dimen_t h; @+HGET32(h); @+hwrite_dimension(h);@+}\ + { dimen_t d; @+if ((I)&b001) HGET32(d); @+ else d=0;@+hwrite_dimension(d); @+}\ + { dimen_t w; @+HGET32(w); @+hwrite_dimension(w);@+} \ +if ((I)&b010) { dimen_t a; @+HGET32(a); hwritef(" shifted"); @+hwrite_dimension(a);@+}\ + { stretch_t p; @+HGET_STRETCH(p);@+hwrite_plus(&p);@+}\ + { stretch_t m; @+HGET_STRETCH(m);@+hwrite_minus(&m);@+}\ + if ((I)&b100) {xdimen_t x;@+ hget_xdimen_node(&x); @+hwrite_xdimen_node(&x);@+} else HGET_REF(xdimen_kind)@;\ + { list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+} +@# + +#define @[HGET_PACK(K,I)@] @/\ + if ((I)&b010) { dimen_t d; @+HGET32(d); hwritef(" shifted"); @+hwrite_dimension(d); @+ }\ + if (K==vpack_kind) { dimen_t d; @+HGET32(d); hwritef(" max depth");@+hwrite_dimension(d); @+ }\ + if ((I)&b001) hwritef(" add");@+ else hwritef(" to");\ + if ((I)&b100) {xdimen_t x;@+ hget_xdimen_node(&x);@+hwrite_xdimen_node(&x);@+}\ + else @+HGET_REF(xdimen_kind);\ + { list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+} +@ + + +\subsection{Leaders}\label{leaders} +Leaders\index{leaders} are a special type of glue that is best explained by a few +examples. +Where as ordinary glue fills its designated space with \hfil\ whiteness,\break +leaders fill their designated space with either a rule \xleaders\hrule\hfil\ or\break +some sort of repeated\leaders\hbox to 15pt{$\hss.\hss$}\hfil content.\break +In multiple leaders, the dots\leaders\hbox to 15pt{$\hss.\hss$}\hfil are usually aligned\index{alignment} across lines,\break +as in the last\leaders\hbox to 15pt{$\hss.\hss$}\hfil three lines.\break +Unless you specify centered\index{centered}\cleaders\hbox to 15pt{$\hss.\hss$}\hfil leaders\break +or you specify expanded\index{expanded}\xleaders\hbox to 15pt{$\hss.\hss$}\hfil leaders.\break +The former pack the repeated content tight and center +the repeated content in the available space, the latter distributes +the extra space between all the repeated instances. + +In the short format, the two lowest info bits store the type +of leaders: 1 for aligned, 2 for centered, and 3 for expanded. +The |b100| info bit is usually set and only zero in the unlikely +case that the glue is zero and therefore not present. + +\readcode +@s LEADERS symbol +@s ALIGN symbol +@s CENTER symbol +@s EXPAND symbol +@s leaders symbol +@s ltype symbol +@= +%token LEADERS "leaders" +%token ALIGN "align" +%token CENTER "center" +%token EXPAND "expand" +%type leaders +%type ltype +@ + +@= +::@=leaders@> :< return LEADERS; >: +::@=align@> :< return ALIGN; >: +::@=center@> :< return CENTER; >: +::@=expand@> :< return EXPAND; >: +@ +@= +ltype: {$$=1;} | ALIGN {$$=1;} @+| CENTER {$$=2;} @+| EXPAND {$$=3;}; +leaders: glue_node ltype rule_node {@+if ($1) $$=$2|b100;@+else $$=$2; @+} + | glue_node ltype hbox_node {@+if ($1) $$=$2|b100;@+else $$=$2;@+} + | glue_node ltype vbox_node {@+if ($1) $$=$2|b100;@+else $$=$2;@+}; +content_node: start LEADERS leaders END @| {@+ hput_tags($1, TAG(leaders_kind, $3));} +@ + +\writecode +@= +void hwrite_leaders_type(int t) +{@+ + if (t==2) hwritef(" center"); + else if (t==3) hwritef(" expand"); +} +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(leaders_kind,1): @+ HGET_LEADERS(1); @+break; +case TAG(leaders_kind,2): @+ HGET_LEADERS(2); @+break; +case TAG(leaders_kind,3): @+ HGET_LEADERS(3); @+break; +case TAG(leaders_kind,b100|1): @+ HGET_LEADERS(b100|1); @+break; +case TAG(leaders_kind,b100|2): @+ HGET_LEADERS(b100|2); @+break; +case TAG(leaders_kind,b100|3): @+ HGET_LEADERS(b100|3); @+break; +@ +@= +#define @[HGET_LEADERS(I)@]@/ \ +if ((I)&b100) hget_glue_node();\ +hwrite_leaders_type((I)&b011);\ +if (KIND(*hpos)==rule_kind) hget_rule_node(); \ +else if (KIND(*hpos)==hbox_kind) hget_hbox_node(); \ +else hget_vbox_node(); +@ + +\subsection{Baseline Skips} +Baseline\index{baseline skip} skips are small amounts of glue inserted +between two consecutive lines of text. To get nice looking pages, the +amount of glue\index{glue} inserted must take into account the depth +of the line above the glue and the height of the line below the glue +to achieve a constant distance of the baselines. For example, if we +have the lines +\medskip + +\qquad\vbox{\hsize=0.5\hsize\noindent +``There is no\hfil\break +more gas\hfil\break +in the tank.'' +}\hss + +\medskip\noindent +\TeX\ will insert 7.69446pt of baseline skip between the first and the +second line and 3.11111pt of baseline skip between the second and the +third line. This is due to the fact that the first line has no +descenders, its depth is zero, the second line has no ascenders but +the ``g'' descends below the baseline, and the third line has +ascenders (``t'', ``h'',\dots) so it is higher than the second line. +\TeX's choice of baseline skips ensures that the baselines are exactly +12pt apart in both cases. + +Things get more complicated if the text contains mathematical formulas because then +a line can get so high or deep that it is impossible to keep the distance between +baselines constant without two adjacent lines touching each other. In such cases, +\TeX\ will insert a small minimum line skip glue\index{line skip glue}. + +For the whole computation, \TeX\ uses three parameters: {\tt base\-line\-skip}, +{\tt line\-skip\-limit},\index{line skip limit} and +{\tt lineskip}. {\tt baselineskip} is a glue value; its size is the +normal distance of two baselines. \TeX\ adjusts the size of the +{\tt baselineskip} glue for the height and the depth of the two lines and +then checks the result against {\tt lineskiplimit}. If the result is +smaller than {\tt lineskiplimit} it will use the {\tt lineskip} glue +instead. + +Because the depth and the height of lines depend on the outcome +of the line breaking\index{line breaking} +routine, baseline computations must be done in the viewer. +The situation gets even more complicated because \TeX\ can manipulate the insertion +of baseline skips in various ways. Therefore \HINT\ requires the insertion of +baseline nodes wherever the viewer is supposed to perform a baseline skip +computation. + +In the short format of a baseline definition, we store only +the nonzero components and use the +info bits to mark them: |b100| implies $|bs|\ne0$, +|b010| implies $|ls|\ne 0$, and |b001| implies $|lslimit|\ne 0$. +If the baseline has only zero components, we put a reference to baseline number 0 +in the output. + +@= +typedef struct {@+ +glue_t bs, ls;@+ +dimen_t lsl;@+ +} baseline_t; +@ + + + +\readcode +@s BASELINE symbol +@s baseline symbol +@= +%token BASELINE "baseline" +%type baseline +@ +@= +::@=baseline@> :< return BASELINE; >: +@ + +@= +baseline: dimension { if ($1!=0) HPUT32($1); } + glue_node glue_node @/{ $$=b000; if ($1!=0) $$|=b001; + if ($3) $$|=b100; + if ($4) $$|=b010; + @+}; +content_node: start BASELINE baseline END @/ +{ @+if ($3==b000) HPUT8(0); @+hput_tags($1,TAG(baseline_kind, $3)); }; +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(baseline_kind,b001): { baseline_t b;@+ HGET_BASELINE(b001,b);@+ }@+break; +case TAG(baseline_kind,b010): { baseline_t b;@+ HGET_BASELINE(b010,b);@+ }@+break; +case TAG(baseline_kind,b011): { baseline_t b;@+ HGET_BASELINE(b011,b);@+ }@+break; +case TAG(baseline_kind,b100): { baseline_t b;@+ HGET_BASELINE(b100,b);@+ }@+break; +case TAG(baseline_kind,b101): { baseline_t b;@+ HGET_BASELINE(b101,b);@+ }@+break; +case TAG(baseline_kind,b110): { baseline_t b;@+ HGET_BASELINE(b110,b);@+ }@+break; +case TAG(baseline_kind,b111): { baseline_t b;@+ HGET_BASELINE(b111,b);@+ }@+break; +@ + +@= +#define @[HGET_BASELINE(I,B)@] \ + if((I)&b001) HGET32((B).lsl); @+else B.lsl=0; hwrite_dimension(B.lsl);\ + if((I)&b100) hget_glue_node(); \ + else {B.bs.p.o=B.bs.m.o=B.bs.w.w=0; @+B.bs.w.h=B.bs.w.v=B.bs.p.f=B.bs.m.f=0.0; @+hwrite_glue_node(&(B.bs));@+}\ + if((I)&b010) hget_glue_node(); \ + else {B.ls.p.o=B.ls.m.o=B.ls.w.w=0; @+B.ls.w.h=B.ls.w.v=B.ls.p.f=B.ls.m.f=0.0; @+hwrite_glue_node(&(B.ls));@+} +@ + + +\putcode +@= +uint8_t hput_baseline(baseline_t *b) +{ info_t info=b000; + if (!ZERO_GLUE(b->bs)) @+info|=b100; + if (!ZERO_GLUE(b->ls)) @+ info|=b010; + if (b->lsl!=0) { @+ HPUT32(b->lsl); @+info|=b001; @+} + return TAG(baseline_kind,info); +} +@ + + + +\subsection{Ligatures} +Ligatures\index{ligature} occur only in horizontal lists. They specify characters +that combine the glyphs of several characters into one specialized +glyph. For example in the word ``{\it difficult\/}'' the three letters +``{\it f{}f{}i\/}'' are combined into the ligature ``{\it ffi\/}''. +Hence, a ligature is very similar to a simple glyph node; the +characters that got replaced are, however, retained in the ligature +because they might be needed for example to support searching. Since +ligatures are therefore only specialized list of characters and since +we have a very efficient way to store such lists of characters, namely +as a |text|, input and output of ligatures is quite simple. + +The info value zero is reserved for references to a ligature. If the +info value is between 1 and 6, it gives the number of bytes used to encode +the characters in UTF8. Note that a ligature will always include a +glyph byte, so the minimum size is 1. A typical ligature like ``{\it fi\/}'' +will need 3 byte: the ligature character ``{\it fi\/}'', and +the replacement characters ``f'' and ''i''. More byte might be +required if the character codes exceed |0x7F| since we use the UTF8 +encoding scheme for larger character codes. If the info value is 7, +a full text node follows the font byte. In the long +format, we give the font, the character code, and then the replacement +characters represented as a text. + +@= +typedef struct{@+uint8_t f; @+list_t l;@+} lig_t; +@ + +\readcode +@s ref symbol +@s LIGATURE symbol +@s ligature symbol +@s cc_list symbol +@s lig_cc symbol +@= +%token LIGATURE "ligature" +%type lig_cc +%type ligature +%type ref +@ +@= +::@=ligature@> :< return LIGATURE; >: +@ + +@=@/ +cc_list:@+ | cc_list TXT_CC { hput_utf8($2); }; +lig_cc: UNSIGNED {RNG("UTF-8 code",$1,0,0x1FFFFF);$$=hpos-hstart; hput_utf8($1); }; +lig_cc: CHARCODE {$$=hpos-hstart; hput_utf8($1); }; +ref: REFERENCE { HPUT8($1); $$=$1; }; +ligature: ref { REF(font_kind,$1);} lig_cc TXT_START cc_list TXT_END @/ + { $$.f=$1; $$.l.p=$3; $$.l.s=(hpos-hstart)-$3; + RNG("Ligature size",$$.l.s,0,255);}; +content_node: start LIGATURE ligature END {hput_tags($1,hput_ligature(&($3)));}; +@ + +\writecode +@= +void hwrite_ligature(lig_t *l) +{ uint32_t pos=hpos-hstart; + hwrite_ref(l->f); + hpos=l->l.p+hstart; + hwrite_charcode(hget_utf8()); + hwritef(" \""); + while (hposl.p+l->l.s) + hwrite_txt_cc(hget_utf8()); + hwritec('"'); + hpos=hstart+pos; +} +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(ligature_kind,1):@+ {lig_t l; @+HGET_LIG(1,l);@+} @+break; +case TAG(ligature_kind,2):@+ {lig_t l; @+HGET_LIG(2,l);@+} @+break; +case TAG(ligature_kind,3):@+ {lig_t l; @+HGET_LIG(3,l);@+} @+break; +case TAG(ligature_kind,4):@+ {lig_t l; @+HGET_LIG(4,l);@+} @+break; +case TAG(ligature_kind,5):@+ {lig_t l; @+HGET_LIG(5,l);@+} @+break; +case TAG(ligature_kind,6):@+ {lig_t l; @+HGET_LIG(6,l);@+} @+break; +case TAG(ligature_kind,7):@+ {lig_t l; @+HGET_LIG(7,l);@+} @+break; +@ +@= +#define @[HGET_LIG(I,L)@] @/\ +(L).f=HGET8;REF(font_kind,(L).f);\ +if ((I)==7) hget_list(&((L).l)); \ +else { (L).l.s=(I); (L).l.p=hpos-hstart; @+ hpos+=(L).l.s;} \ +hwrite_ligature(&(L)); +@ + +\putcode +@= +uint8_t hput_ligature(lig_t *l) +{ @+if (l->l.s < 7) return TAG(ligature_kind,l->l.s); + else@/ + { uint32_t pos=l->l.p; + hput_tags(pos,hput_list(pos+1, &(l->l))); + return TAG(ligature_kind,7); + } +} +@ + + +\subsection{Discretionary breaks}\label{discbreak}\index{discretionary break} +\HINT\ is capable to break lines into paragraphs. It does this +primarily at interword spaces but it might also break a line in the +middle of a word if it finds a discretionary\index{discretionary break} +line break there. These discretionary breaks are usually +provided by an automatic hyphenation algorithm but they might be also +explicitly\index{explicit} inserted by the author of a +document. + +When a line break occurs at such a discretionary break, the line +before the break ends with a |pre_break| list of nodes, the line after +the break starts with a |post_break| list of nodes, and the next +|replace_count| nodes after the discretionary break will be +ignored. Both lists must consist entirely of glyphs\index{glyph}, +kerns\index{kern}, boxes\index{box}, rules\index{rule}, or +ligatures\index{ligature}. For example, an ordinary discretionary +break will have a |pre_break| list containing ``-'', an empty +|post_break| list, and a |replace_count| of zero. + +The long format starts with an optional ``{\tt !}'', indicating an +explicit discretionary break, followed by the replace-count. +Then comes the pre-break list followed by the post-break list. +The replace-count can be omitted if it is zero; +an empty post-break list may be omitted as well. +Both list may be omitted only if both are empty. + +In the short format, the three components of a disc node are stored +in this order: |replace_count|, |pre_break| list, and |post_break| list. +The |b100| bit in the info value indicates the presence of a replace-count, +the |b010| bit the presence of a |pre_break| list, +and the |b001| bit the presence of a |post_break| list. +Since the info value |b000| is reserved for references, at least one +of these must be specified; so we represent a node with empty lists +and a replace\index{replace count} count of zero using the info value +|b100| and a zero byte for the replace count. + +Replace counts must be in the range 0 to 31; so the short format can +set the high bit of the replace count to indicate an explicit\index{explicit} break. + +@= +typedef struct disc_t@+ {@+ bool x; @+list_t p,q;@+ uint8_t r;@+ } disc_t; +@ + + +\readcode +@s DISC symbol +@s disc symbol +@s disc_node symbol +@s replace_count symbol + +@= +%token DISC "disc" +%type disc +%type replace_count +@ +@= +::@=disc@> :< return DISC; >: +@ + +@=@/ +replace_count: explicit {@+ if ($1) {$$=0x80; HPUT8(0x80);@+}@+ else $$=0x00;@+} + | explicit UNSIGNED { RNG("Replace count",$2,0,31); + $$=($2)|(($1)?0x80:0x00); @+ if ($$!=0) HPUT8($$);@+}; +disc: replace_count list list { $$.r=$1;$$.p=$2; $$.q=$3; + if ($3.s==0) { hpos=hpos-2;@+ if ($2.s==0) hpos=hpos-2; @+}@+} + | replace_count list { $$.r=$1;$$.p=$2; if ($2.s==0) hpos=hpos-2;@+ $$.q.s=0; } + | replace_count { $$.r=$1;$$.p.s=0; $$.q.s=0; }; + + +disc_node: start DISC disc END + {hput_tags($1,hput_disc(&($3)));}; + +content_node: disc_node; +@ + +\writecode +@= +void hwrite_disc(disc_t *h) +{ @+hwrite_explicit(h->x); + if (h->r!=0) hwritef(" %d",h->r); + if (h->p.s!=0 || h->q.s!=0) hwrite_list(&(h->p)); + if (h->q.s!=0) hwrite_list(&(h->q)); +} +void hwrite_disc_node(disc_t *h) +{ @+ hwrite_start(); @+hwritef("disc"); @+ hwrite_disc(h); @+hwrite_end();} +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(disc_kind,b001): {disc_t h; @+HGET_DISC(b001,h);@+ hwrite_disc(&h); @+} @+break; +case TAG(disc_kind,b010): {disc_t h; @+HGET_DISC(b010,h);@+ hwrite_disc(&h); @+} @+break; +case TAG(disc_kind,b011): {disc_t h; @+HGET_DISC(b011,h);@+ hwrite_disc(&h); @+} @+break; +case TAG(disc_kind,b100): {disc_t h; @+HGET_DISC(b100,h);@+ hwrite_disc(&h); @+} @+break; +case TAG(disc_kind,b101): {disc_t h; @+HGET_DISC(b101,h);@+ hwrite_disc(&h); @+} @+break; +case TAG(disc_kind,b110): {disc_t h; @+HGET_DISC(b110,h);@+ hwrite_disc(&h); @+} @+break; +case TAG(disc_kind,b111): {disc_t h; @+HGET_DISC(b111,h);@+ hwrite_disc(&h); @+} @+break; +@ + +@= +#define @[HGET_DISC(I,Y)@]\ +if ((I)&b100) {uint8_t r=HGET8; (Y).r=r&0x7F; @+ RNG("Replace count",(Y).r,0,31); @+(Y).x=(r&0x80)!=0; @+}\ +@+else { (Y).r=0; @+ (Y).x=false;@+}\ +if ((I)&b010) hget_list(&((Y).p)); else { (Y).p.p=hpos-hstart; @+(Y).p.s=0; @+(Y).p.k=list_kind; @+}\ +if ((I)&b001) hget_list(&((Y).q)); else { (Y).q.p=hpos-hstart; @+(Y).q.s=0; @+(Y).q.k=list_kind; @+} +@ + +@= +void hget_disc_node(disc_t *h) +{ @@; + if (KIND(a)!=disc_kind || INFO(a)==b000) + QUIT("Hyphen expected at 0x%x got %s,%d",node_pos,NAME(a),INFO(a)); + HGET_DISC(INFO(a),*h); + @@; +} +@ + +When |hput_disc| is called, the node is already written to the output, +but empty lists might have been deleted, and the info value needs to be determined. +Because the info value |b000| is reserved for references, a zero reference +count is written to avoid this case. +\putcode +@= +uint8_t hput_disc(disc_t *h) +{ info_t info=b000; + if (h->r!=0) info|=b100; + if (h->q.s!=0) info|=b011; + else if (h->p.s!=0) info|=b010; + if (info==b000) { @+info|=b100; @+HPUT8(0);@+} + return TAG(disc_kind,info); +} +@ +\subsection{Paragraphs} +The most important procedure that the \HINT\ viewer inherits from \TeX\ is the +line breaking routine. If the horizontal size of the paragraph is not known, +breaking the paragraph\index{paragraph} into lines must be postponed and this is done by creating +a paragraph node. The paragraph node must contain all information that \TeX's +line breaking\index{line breaking} algorithm needs to do its job. + +Besides the horizontal list describing the content of the paragraph and +the extended dimension describing the horizontal size, +this is the set of parameters that guide the line breaking algorithm: + +\itemize +\item +Integer parameters:\hfill\break +{\tt pretolerance} (badness tolerance before hyphenation),\hfill\break +{\tt tolerance} (badness tolerance after hyphenation),\hfill\break +{\tt line\_penalty} (added to the badness of every line, increase to get fewer lines),\hfill\break +{\tt hy\-phen\_pe\-nal\-ty} (penalty for break after hyphenation break),\hfill\break +{\tt ex\_hy\-phen\_pe\-nal\-ty} (penalty for break after explicit\index{explicit} break),\hfill\break +{\tt doub\-le\_hy\-phen\_de\-merits} (demerits for double hyphen break),\hfill\break +{\tt final\_hyphen\_de\-me\-rits} (demerits for final hyphen break),\hfill\break +{\tt adj\_demerits} (demerits for adjacent incompatible lines),\hfill\break +{\tt looseness} (make the paragraph that many lines longer than its optimal size),\hfill\break +{\tt inter\_line\_penalty} (additional penalty between lines),\hfill\break +{\tt club\_pe\-nal\-ty} (penalty for creating a club line),\hfill\break +{\tt widow\_penalty} (penalty for creating a widow line),\hfill\break +{\tt display\_widow\_penalty} (ditto, just before a display),\hfill\break +{\tt bro\-ken\_pe\-nal\-ty} (penalty for breaking a page at a broken line),\hfill\break +{\tt hang\_af\-ter} (start/end hanging indentation at this line). +\item +Dimension parameters:\hfill\break +{\tt line\_skip\_limit} (threshold for {\tt line\_skip} instead of {\tt base\-line\_skip}),\hfill\break +{\tt hang\_in\-dent} (amount of hanging indentation),\hfill\break +{\tt emergency\_stretch} (stretchability added to every line in the final pass of line breaking). +\item +Glue parameters:\hfill\break +{\tt baseline\_skip} (desired glue between baselines),\hfill\break +{\tt line\_skip} (interline glue if {\tt baseline\_skip} is infeasible),\hfill\break +{\tt left\_skip} (glue at left of justified lines),\hfill\break +{\tt right\_skip} (glue at right of justified lines),\hfill\break +{\tt par\_fill\_skip} (glue on last line of paragraph). +\enditemize + + +For a detailed explanation of these parameters and how they influence +line breaking, you should consult the {\TeX}book\cite{DK:texbook}; +\TeX's {\tt parshape} feature is currently not implemented. There are +default values for all of these parameters (see section~\secref{defaults}), +and therefore it might not be necessary to specify any of them. +Any local adjustments are contained in a list of +parameters contained in the paragraph node. + +A further complication arises from displayed\index{displayed formula} formulas +that interrupt a paragraph. Such displays are described in the next +section. + +To summarize, a paragraph node in the long format specifies an +extended dimension, a parameter list, +and a node list. The extended dimension is given either as an +|xdimen| node (info bit |b100|) or as a reference; similarly the parameter list +can be embedded in the node (info bit |b010|) or again it is given by a reference. + + +\readcode +@s PAR symbol +@s par symbol +@s xdimen_ref symbol +@s param_ref symbol +@s par_dimen symbol + + +@= +%token PAR "par" +%type par +@ + +@= +::@=par@> :< return PAR; >: +@ + + +The following parsing rules are slightly more complicated than I would like them to be, but it seems more important +to achieve a regular layout of the short format nodes where all sub nodes are located at the end of a node. +In this case, I want to put a |param_ref| before an |xdimen| node, but otherwise have +the |xdimen_ref| before a |param_list|. +The |par_dimen| rule is introduced only to avoid a reduce/reduce conflict in the parser. +The parsing of |empty_param_list| and |non_empty_param_list| is explained in +section~\secref{paramlist}. + +@= +par_dimen: xdimen { hput_xdimen_node(&($1)); }; +par: xdimen_ref param_ref list {$$=b000;} + | xdimen_ref empty_param_list non_empty_param_list list { $$=b010;} + | xdimen_ref empty_param_list list { $$=b010;} + | xdimen param_ref { hput_xdimen_node(&($1)); } list { $$=b100;} + | par_dimen empty_param_list non_empty_param_list list { $$=b110;} + | par_dimen empty_param_list list { $$=b110;}; + +content_node: start PAR par END { hput_tags($1,TAG(par_kind,$3));}; +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(par_kind,b000): @+HGET_PAR(b000);@+break; +case TAG(par_kind,b010): @+HGET_PAR(b010);@+break; +case TAG(par_kind,b100): @+HGET_PAR(b100);@+break; +case TAG(par_kind,b110): @+HGET_PAR(b110);@+break; +@ + +@= +#define @[HGET_PAR(I)@] @/\ +{ uint8_t n;\ + if ((I)==b100) {n=HGET8; @+REF(param_kind,n);@+}\ + if ((I)&b100) {xdimen_t x; @+hget_xdimen_node(&x); @+hwrite_xdimen(&x);@+} else HGET_REF(xdimen_kind);\ + if ((I)&b010) { list_t l; @+hget_param_list(&l); @+hwrite_param_list(&l); @+} \ + else if ((I)!=b100) HGET_REF(param_kind)@; else hwrite_ref(n);\ + { list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+}} +@ + + +\subsection{Mathematics}\index{Mathematics}\index{displayed formula} +\gdef\subcodetitle{Displayed Math} +Being able to handle mathematics\index{mathematics} nicely is one +of the primary features of \TeX\ and +so you should expect the same from \HINT. +We start here with the more complex case---displayed equations---and finish with the +simpler case of mathematical formulas that are part of the normal flow of text. + +Displayed equations occur inside a paragraph\index{paragraph} +node. They interrupt normal processing of the paragraph and the +paragraph processing is resumed after the display. Positioning of the +display depends on several parameters, the shape of the paragraph, and +the length of the last line preceding the display. Displayed formulas +often feature an equation number which can be placed either left or +right of the formula. Also the size of the equation number will +influence the placement of the formula. + +In a \HINT\ file, the parameter list is followed by a list of content +nodes, representing the formula, and an optional horizontal box +containing the equation number. + +In the short format, we use the info bit |b100| to indicate the +presence of a parameter list (which might be empty---so it's actually the absence of a +reference to a parameter list); the info bit |b010| to indicate the presence of +a left equation number; and the info bit |b001| for a right +equation\index{equation number} number. + +In the long format, we use ``{\tt eqno}'' or ``{\tt left eqno}'' to indicate presence and +placement of the equation number. + +\readcode +@s MATH symbol +@s math symbol +@= +%token MATH "math" +%type math +@ + +@= +::@=math@> :< return MATH; >: +@ + +@= +math: param_ref list {$$=b000;} + | param_ref list hbox_node {$$=b001;} + | param_ref hbox_node list {$$=b010;} + | empty_param_list list {$$=b100;} + | empty_param_list list hbox_node {$$=b101;} + | empty_param_list hbox_node list {$$=b110;} + | empty_param_list non_empty_param_list list {$$=b100;} + | empty_param_list non_empty_param_list list hbox_node {$$=b101;} + | empty_param_list non_empty_param_list hbox_node list {$$=b110;}; + +content_node: start MATH math END @/{ hput_tags($1,TAG(math_kind,$3));}; +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(math_kind,b000): HGET_MATH(b000); @+ break; +case TAG(math_kind,b001): HGET_MATH(b001); @+ break; +case TAG(math_kind,b010): HGET_MATH(b010); @+ break; +case TAG(math_kind,b100): HGET_MATH(b100); @+ break; +case TAG(math_kind,b101): HGET_MATH(b101); @+ break; +case TAG(math_kind,b110): HGET_MATH(b110); @+ break; +@ + +@= +#define @[HGET_MATH(I)@] \ +if ((I)&b100) { list_t l; @+hget_param_list(&l); @+hwrite_param_list(&l); @+} \ +else HGET_REF(param_kind);\ +if ((I)&b010) hget_hbox_node(); \ +{ list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+} \ +if ((I)&b001) hget_hbox_node(); +@ + +\gdef\subcodetitle{Text Math} +Things are much simpler if mathematical formulas are embedded in regular text. +Here it is just necessary to mark the beginning and the end of the formula +because glue inside a formula is not a possible point for a line break. +To break the line within a formula you can insert a penalty node. + +In the long format, such a simple math node just consists of the keyword ``on'' +or ``off''. In the short format, there are two info values still unassigned: +we use |b011| for ``off'' and |b111| for ``on''. + + +\readcode +@s ON symbol +@s OFF symbol +@s on_off symbol +@= +%token ON "on" +%token OFF "off" +%type on_off +@ + +@= +::@=on@> :< return ON; >: +::@=off@> :< return OFF; >: +@ + +@= +on_off: ON {$$=1;} | OFF {$$=0;}; +math: on_off { $$=b011|($1<<2); }; +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(math_kind,b111): hwritef(" on");@+break; +case TAG(math_kind,b011): hwritef(" off");@+break; +@ + +Note that \TeX\ allows math nodes to specify a width using the current value of +mathsurround. If this width is nonzero, it is equivalent to inserting a +kern node before the math on node and after the math off node. + +\subsection{Adjustments}\label{adjust} +An adjustment\index{adjustment} occurs only in paragraphs\index{paragraph}. +When the line breaking\index{line breaking} routine finds an adjustment, it inserts +the vertical material contained in the adjustment node right after the current line. +Adjustments simply contain a list node. + +\vbox{\readcode\vskip -\baselineskip\putcode} +@s ADJUST symbol +@= +%token ADJUST "adjust" +@ + +@= +::@=adjust@> :< return ADJUST; >: +@ + +@= +content_node: start ADJUST list END { hput_tags($1,TAG(adjust_kind,1));}; +@ + +\vbox{\getcode\vskip -\baselineskip\writecode} +@= +@t\1\kern1em@> +case TAG(adjust_kind,1):@+ { list_t l;@+hget_list(&l); @+ hwrite_list(&l); @+} @+ break; +@ + +\subsection{Tables}\index{alignment} +As long as a table contains no dependencies on \.{hsize} and \.{vsize}, +Hi\TeX\ can expand an alignment into a set of nested horizontal and +vertical boxes and no special processing is required. +As long as only the size of the table itself but neither the tabskip +glues nor the table content depends on \.{hsize} or \.{vsize}, the table +just needs an outer node of type |hset_kind| or |vset_kind|. If there +is non aligned material inside the table that depends on \.{hsize} or +\.{vsize}, a vpack or hpack node is still sufficient. + +While it is reasonable to restrict the tabskip glues to be ordinary +glue values without \.{hsize} or \.{vsize} dependencies, it might be +desirable to have content in the table that does depend on \.{hsize} or +\.{vsize}. For the latter case, we need a special kind of table +node. Here is why: + +As soon as the dimension of an item in the table is an extended +dimension, it is no longer possible to compute the maximum natural with +of a column, because it is not possible to compare extended dimensions +without knowing \.{hsize} and \.{vsize}. Hence the computation of maximum +widths needs to be done in the viewer. After knowing the width of the columns, +the setting of tabskip glues is easy to compute. + +To implement these extended tables, we will need a table node that +specifies a direction, either horizontal or vertical; a list of +tabskip glues, with the provision that the last tabskip glue in the +list is repeated as long as necessary; and a list of table content. +The table's content is stacked, either vertical or +horizontal, orthogonal to the alignment direction of the table. +The table's content consists of nonaligned content, for example extra glue +or rules, and aligned content. +Each element of aligned content +is called an outer item and it consist of a list of inner items. +For example in a horizontal alignment, each row is an outer item +and each table entry in that row is an inner item. +An inner item contains a box node (of kind |hbox_kind|, |vbox_kind|, +|hset_kind|, |vset_kind|, |hpack_kind|, or |vpack_kind|) followed by +an optional span count. + +The glue of the boxes in the inner items will be reset so that all boxes in the same +column reach the same maximum column with. The span counts will be replaced by +the appropriate amount of empty boxes and tabskip glues. Finally the +glue in the outer item will be set to obtain the desired size +of the table. + +The definitions below specify just a |list| for the list of tabskip glues and a +list for the outer table items. +This is just for convenience; the first list must contain glue +nodes and the second list must contain nonaligned content and inner item nodes. + +We reuse the |H| and |V| tokens, defined as part of the specification +of extended dimensions, to indicate the alignment direction of the +table. To tell a reference to an extended dimension from a reference +to an ordinary dimension, we prefix the former with an |XDIMEN| token; +for the latter, the |DIMEN| token is optional. The scanner will +recognize not only ``item'' as an |ITEM| token but also ``row'' and +''column''. This allows a more readable notation, for example by +marking the outer items as rows and the inner items as columns. + +In the short format, the |b010| bit is used to mark a vertical table +and the |b101| bits indicate how the table size is specified; an outer +item node has the info value |b000|, an inner item node with info +value |b111| contains an extra byte for the span count, otherwise the +info value is equal to the span count. + + + + + + +\readcode +@s TABLE symbol +@s ITEM symbol +@s table symbol +@s span_count symbol + +@= +%token TABLE "table" +%token ITEM "item" +%type table span_count +@ + +@= +::@=table@> :< return TABLE; >: +::@=item@> :< return ITEM; >: +::@=row@> :< return ITEM; >: +::@=column@> :< return ITEM; >: +@ + +@= +span_count: UNSIGNED { $$=hput_span_count($1); }; +content_node: start ITEM content_node END { hput_tags($1,TAG(item_kind,1)); }; +content_node: start ITEM span_count content_node END {@+ hput_tags($1,TAG(item_kind,$3));}; +content_node: start ITEM list END { hput_tags($1,TAG(item_kind,b000));}; + +table: H box_goal list list {$$=$2;}; +table: V box_goal list list {$$=$2|b010;}; + +content_node: start TABLE table END { hput_tags($1,TAG(table_kind,$3));}; +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(table_kind,b000): @+ HGET_TABLE(b000); @+ break; +case TAG(table_kind,b001): @+ HGET_TABLE(b001); @+ break; +case TAG(table_kind,b010): @+ HGET_TABLE(b010); @+ break; +case TAG(table_kind,b011): @+ HGET_TABLE(b011); @+ break; +case TAG(table_kind,b100): @+ HGET_TABLE(b100); @+ break; +case TAG(table_kind,b101): @+ HGET_TABLE(b101); @+ break; +case TAG(table_kind,b110): @+ HGET_TABLE(b110); @+ break; +case TAG(table_kind,b111): @+ HGET_TABLE(b111); @+ break;@# + +case TAG(item_kind,b000): @+{@+ list_t l;@+ hget_list(&l);@+ hwrite_list(&l);@+ } @+ break; +case TAG(item_kind,b001): hget_content_node(); @+ break; +case TAG(item_kind,b010): hwritef(" 2");@+hget_content_node(); @+ break; +case TAG(item_kind,b011): hwritef(" 3");@+hget_content_node(); @+ break; +case TAG(item_kind,b100): hwritef(" 4");@+hget_content_node(); @+ break; +case TAG(item_kind,b101): hwritef(" 5");@+hget_content_node(); @+ break; +case TAG(item_kind,b110): hwritef(" 6");@+hget_content_node(); @+ break; +case TAG(item_kind,b111): hwritef(" %u",HGET8);@+hget_content_node(); @+ break; +@ + +@= +#define @[HGET_TABLE(I)@] \ +if(I&b010) hwritef(" v"); @+else hwritef(" h"); \ +if ((I)&b001) hwritef(" add");@+ else hwritef(" to");\ +if ((I)&b100) {xdimen_t x; hget_xdimen_node(&x); @+hwrite_xdimen_node(&x);@+} else HGET_REF(xdimen_kind)@;\ +{@+ list_t l; @+hget_list(&l);@+ hwrite_list(&l);@+ } /* tabskip */ \ +{@+ list_t l; @+hget_list(&l);@+ hwrite_list(&l);@+ } /* items */ +@ + + +\putcode +@= +info_t hput_span_count(uint32_t n) +{ if (n==0) QUIT("Span count in item must not be zero"); + else if (n<7) return n; + else if (n>0xFF) QUIT("Span count %d must be less than 255",n); + else + { HPUT8(n); return 7; } +} +@ +\section{Extensions to \TeX}\hascode + +\subsection{Images} +Images behave pretty much like glue\index{glue}. They can stretch (or shrink) +together with the surrounding glue to fill a horizontal or vertical box. +Like glue, they stretch in the horizontal direction when filling an horizontal box +and they stretch in the vertical direction as part of a vertical box. +Stretchability and shrinkability are optional parts of an image node. + +Unlike glue, images have both a width and a height. +The relation of height to width, the aspect ratio, is preserved by stretching and shrinking. + +While glue often has a zero width, images usually have a nonzero natural size and making +them much smaller is undesirable. +The natural width and height of an image are optional parts of an image node; +typically this information is contained in the image data. + +The only required part of an image node is the number of the auxiliary section +where the image data can be found. + +@= +typedef struct {@+ +uint16_t n;@+ +dimen_t w,h;@+ +stretch_t p,m;@+ +} image_t; +@ + + +\readcode +@s IMAGE symbol +@s image symbol +@s image_dimen symbol +@= +%token IMAGE "image" +%type image image_dimen +@ + +@= +::@=image@> :< return IMAGE; >: +@ + +@= +image_dimen: dimension dimension {$$.w=$1; $$.h=$2;} | {$$.w=$$.h=0; }; +image: UNSIGNED image_dimen plus minus { $$.w=$2.w; $$.h=$2.h; $$.p=$3; $$.m=$4; RNG("Section number",$1,3,max_section_no);$$.n=$1; }; +content_node: start IMAGE image END { hput_tags($1,hput_image(&($3)));} +@ + +\writecode +@= +void hwrite_image(image_t *x) +{ hwritef(" %u",x->n); + if (x->w!=0 ||x->h!=0) { hwrite_dimension(x->w); hwrite_dimension(x->h);@+} + hwrite_plus(&x->p); + hwrite_minus(&x->m); +} +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(image_kind,b100): @+ { image_t x;@+HGET_IMAGE(b100,x);@+}@+break; +case TAG(image_kind,b101): @+ { image_t x;@+HGET_IMAGE(b101,x);@+}@+break; +case TAG(image_kind,b110): @+ { image_t x;@+HGET_IMAGE(b110,x);@+}@+break; +case TAG(image_kind,b111): @+ { image_t x;@+HGET_IMAGE(b111,x);@+}@+break; +@ + +@= +#define @[HGET_IMAGE(I,X)@] @/\ +HGET16((X).n);RNG("Section number",(X).n,3,max_section_no); \ +if (I&b010) {HGET32((X).w);HGET32((X).h);@+} \ +else (X).w=(X).h=0;\ +if (I&b001) {HGET_STRETCH((X).p);HGET_STRETCH((X).m);@+}\ +else { (X).p.f=(X).m.f=0.0; (X).p.o=(X).m.o=normal_o;@+}\ +hwrite_image(&(X)); +@ + + +\putcode +@= +uint8_t hput_image(image_t *x) +{ info_t i=b100; + HPUT16(x->n); + if (x->w!=0 || x->h!=0)@+ {HPUT32(x->w); HPUT32(x->h); i|=b010;@+ } + if (x->p.f!=0.0 || x->m.f!=0.0)@+ {hput_stretch(&x->p); hput_stretch(&x->m); i|=b001;@+ } + return TAG(image_kind,i); +} +@ + + +\subsection{Positions, Outlines, Links, and Labels}\label{labels} +\index{position}\index{outline}\index{link}\index{label} +A viewer can usually not display the entire content section of +a \HINT\ file. Instead it will display a page of content and will give +its user various means to change the page. This might be as simple as +a ``page down'' or ``page up'' button (or gesture) and as +sophisticated as searching using regular expressions. More +traditional ways to navigate the content include the use of a table of +content or an index of keywords. All these methods of changing a page +have in common that a part of the content that fits nicely in the +screen area provided by the output device must be rendered given a +position inside the content section. + + +Let's assume that the viewer uses a \HINT\ file in short +format---after all that's the format designed for precisely this use. +A position inside the content section is then the position of the +starting byte of a node. Such a position can be stored as a 32 bit +number. Because even the smallest node contains two tag bytes, +the position of any node is strictly smaller than the maximum 32 bit +number which we can conveniently use as a ``non position''. + +@= +#define HINT_NO_POS 0xFFFFFFFF +@ + +To render a page starting at a given position is not difficult: +We just read content nodes, starting at the given position and feed +them to \TeX's page builder until the page is complete. To implement a +``clickable'' table of content this is good enough. We store with +every entry in the table of content the position of the section +header, and when the user clicks the entry, the viewer can display a +new page starting exactly with that section header. + +Things are slightly more complex if we want to implement a ``page +down'' button. If we press this button, we want the next page to +start exactly where the current page has ended. This is +typically in the middle of a paragraph node, and it might even be in +the middle of an hyphenated word in that paragraph. Fortunately, +paragraph and table nodes are the only nodes that can be broken across page +boundaries. But broken paragraph nodes are a common case non the less, +and unless we want to search for the enclosing node, we need to +augment in this case the primary 32 bit position inside the content +section with a secondary position. Most of the +time, 16 bit will suffice for this secondary position if we give it +relative to the primary position. Further, if the list of nodes forming the +paragraph is given as a text, we need to know the current font at the +secondary position. Of course, the viewer can find it by scanning the +initial part of the text, but when we think of a page down button, the +viewer might already know it from rendering the previous page. + +Similar is the case of a ``page up'' button. Only here we need a page +that ends precisely where our current page starts. Possibly even with +the initial part of a hyphenated word. Here we need a reverse version +of \TeX's page builder that assembles a ``good'' page from the bottom +up instead of from the top down. Sure the viewer can cache the start +position of the previous page (or the rendering of the entire page) if +the reader has reached the current page using the page down +button. But this is not possible in all cases. The reader might have +reached the current page using the table of content or even an index +or a search form. + +This is the most complex case to consider: a link from an index or a +search form to the position of a keyword in the main text. Let's assume +someone looks up the word ``M\"unchen''. Should the viewer then +generate a page that starts in the middle of a sentence with the word +``M\"unchen''? Probably not! We want a page that shows at least the whole sentence if +not the whole paragraph. Of course the program that generates the +link could specify the position of the start of the paragraph instead +of the position of the word. But that will not solve the problem. Just +imagine reading the groundbreaking masterpiece of a German philosopher +on a small hand-held device: the paragraph will most likely be very +long and perhaps only part of the first sentence will fit on the small +screen. So the desired keyword might not be found on the page that +starts with the beginning of the paragraph; it might not even be on +the next or next to next page. Only the viewer can decide what is the +best fragment of content to display around the position of the given +keyword. + +To summarize, we need three different ways to render a page for a given position: +\itemize +\item A page that starts exactly at the given position. +\item A page that ends exactly at the given position. +\item The ``best'' page that contains the given position somewhere in the middle. +\enditemize + +\noindent +A possible way to find the ``best'' page for the latter case +could be the following: +\itemize +\item If the position is inside a paragraph, break the paragraph + into lines. One line will contain + the given position. Let's call this the destination line. +\item If the paragraph will not fit entirely on the page, + start the page with the beginning of the + paragraph if that will place the destination line on the page, otherwise + start with a line in the paragraph that is about half a page + before the destination line. +\item Else traverse the content list backward for about $2/3$ of the + page height and forward for about $2/3$ of the page height, searching + for the smallest negative penalty node. Use the penalty node found as + either the beginning or ending of the page. +\item If there are several equally low negative penalty nodes. Prefer + penalties preceding the destination line over penalty nodes following + it. A good page start is more important than a good page end. +\item If there are are still several equally low negative penalty + nodes, choose the one whose distance to the destination line is closest + to $1/2$ of the page height. +\item If no negative penalty nodes could be found, start the page with + the paragraph containing the destination line. +\item Once the page start (or end) is found, use \TeX's page builder + (or its reverse variant) to complete the page. +\enditemize + +We call content nodes that reference some position inside the content section +``link'' nodes. The position that is referenced is called the destination of the link. +Link nodes occur always in pairs of an ``on'' link +followed by a corresponding ``off'' link that both reference the same position +%, the same nesting level, % not sure! +and no other link nodes between them. +The content between the two will constitute the visible part of the link. + +To encode a position inside the content section that can be used +as the destination of a link node, an other kind of node is needed which +we call a ``label''. + +Links are not the only way to navigate inside a large +document. The user interface can also present an ``outline'' +of the document that can be used for navigation. +An outline node implements an association between a name displayed by the +user interface of the \HINT\ viewer and the destination position in the \HINT\ document. + +It is possible though that outline nodes, link nodes, and label nodes can share +the same kind-value and we have |outline_kind==link_kind==label_kind|. +To distinguish an outline node from a label node---both occur +in the short format definition section---the |b100| info bit is set in an +outline node. + + +@= +void hget_outline_or_label_def(info_t i, uint32_t node_pos) +{ @+if (i&b100) + @@; + else + @@; +} +@ + +The next thing we need to implement is a new maximum number +for outline nodes. We store this number in the variable +|max_outline| and limit it to a 16 bit value. + +In the short format, the value of |max_outline| is stored with the +other maximum values using the kind value |outline_kind==label_kind| and the info +value |b100| for single byte and |b101| for a two byte value. + +\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{7}{Special Maximum Values} +@= +@t\1\kern1em@> +case TAG(outline_kind,b100): +case TAG(outline_kind,b101): max_outline=n; + DBG(DBGDEF|DBGLABEL,"max(outline) = %d\n",max_outline); break; +@ + +\codesection{\putsymbol}{Writing the Short Format}\putindex{1}{7}{Special Maximum Values} +@= +if (max_outline>-1) +{ uint32_t pos=hpos++-hstart; + DBG(DBGDEF|DBGLABEL,"max(outline) = %d\n",max_outline); + hput_tags(pos,TAG(outline_kind,b100|(hput_n(max_outline)-1))); +} +@ + +\codesection{\wrtsymbol}{Writing the Long Format}\wrtindex{1}{7}{Special Maximum Values} +@= +@t\1\kern1em@> +case label_kind: +if (max_ref[label_kind]>-1)@/ +{ hwrite_start(); + hwritef("label %d",max_ref[label_kind]); + hwrite_end();@+ +} +if (max_outline>-1)@/ +{ hwrite_start(); + hwritef("outline %d", max_outline); + hwrite_end();@+ +} +break; +@ + +\codesection{\redsymbol}{Reading the Long Format}\redindex{1}{7}{Special Maximum Values} +@= +max_value: OUTLINE UNSIGNED { max_outline=$2; + RNG("max outline",max_outline,0, 0xFFFF); + DBG(DBGDEF|DBGLABEL,"Setting max outline to %d\n",max_outline); + }; +@ + +After having seen the maximum values, we now explain labels, then links, +and finally outlines. + + +To store labels, we define a data type |label_t| and an array |labels| +indexed by the labels reference number. + +@= +typedef struct +{@+ uint32_t pos; /* position */ + uint8_t where; /* where on the rendered page */ + bool used; /* label used in a link or an outline */ + int next; /* reference in a linked list */ + uint32_t pos0;@+ uint8_t f; /* secondary position */ +} label_t; +@ + +The |where| field indicates where the label position +should be on the rendered page: at the top, +at the bottom, or somewhere in the middle. +An undefined label has |where| equal to zero. + +@= +#define LABEL_UNDEF 0 +#define LABEL_TOP 1 +#define LABEL_BOT 2 +#define LABEL_MID 3 +@ + +@= +label_t *labels; +int first_label=-1; +@ +The variable |first_label| will be used together with the |next| field of +a label to construct a linked list of labels. + +@= +if (max_ref[label_kind]>=0)@/ + ALLOCATE(labels,max_ref[label_kind]+1,label_t); +@ + +The implementation of labels has to solve the +problem of forward links: +a link node that references a label +that is not yet defined. +We solve this problem by +keeping all labels in the definition section. +So for every label at least a definition is available +before we start with the content section and we can fill +in the position when the label is found. +If we restrict labels to the definition section and +do not have an alternative representation, the number of possible references +is a hard limit on the number of labels in a document. +Therefore label references are allowed to use 16 bit reference numbers. +In the short format, +the |b001| bit indicates a two byte reference number if set, and a one byte +reference number otherwise. + +In the short format, the complete information about a label is in the definition section. +In the long format, this is not possible because we do not have node positions. +Therefore we will put label nodes at appropriate points in the content section +and compute the label position when writing the short format. + +\gdef\subcodetitle{Labels} +\readcode +@s LABEL symbol +@s BOT symbol +@s MID symbol +@s placement symbol + +@= +%token LABEL "label" +%token BOT "bot" +%token MID "mid" +%type placement +@ + +@= +::@=label@> :< return LABEL; >: +::@=bot@> :< return BOT; >: +::@=mid@> :< return MID; >: +@ + +A label node specifies the reference number and a placement. + +@= +placement: TOP {$$=LABEL_TOP;} | BOT {$$=LABEL_BOT;} | MID {$$=LABEL_MID;} | {$$=LABEL_MID;}; +content_node: START LABEL REFERENCE placement END @| + { hset_label($3,$4); @+} +@ + + +After parsing a label, the function |hset_label| is called. + +@= +void hset_label(int n,int w ) +{ label_t *t; + REF_RNG(label_kind,n); + t=labels+n;@/ + if (t->where!=LABEL_UNDEF) + MESSAGE("Duplicate definition of label %d\n",n); + t->where=w; + t->pos=hpos-hstart; + t->pos0=hpos0-hstart; + t->next=first_label; first_label=n; +} +@ + + +All that can be done by the above function +is storing the data obtained in the |labels| array. +The generation of the short format output is +postponed until the entire content section has been parsed and +the positions of all labels are known. + +One more complication needs to be considered: The |hput_list| function +is allowed to move lists in the output stream and if positions +inside the list were recorded in a label, these labels need an +adjustment. To find out quickly if any labels are affected, +the |hset_label| function +constructs a linked list of labels starting with the reference number +of the most recent label in |first_label| and the +reference number of the label preceding label |i| in |labels[i].next|. +Because labels are recorded with increasing positions, +the list will be sorted with positions decreasing. + +@= +{ int i; + for (i=first_label;i>=0 && labels[i].pos>=l->p;i=labels[i].next) + { DBG(DBGNODE|DBGLABEL,"Moving label *%d by %d\n", i,d);@/ + labels[i].pos+=d; + if (labels[i].pos0>=l->p) labels[i].pos0+=d; + } +} +@ + + +The |hwrite_label| function\label{hwritelabel} is the reverse of the above parsing rule. +Note that it is different from the +usual |hwrite_|\dots\ functions. And we will see shortly why that is so. + +%see |hwrite_range| +\writecode +@= +void hwrite_label(void) /* called in |hwrite_end| and at the start of a list */ +{@+ while (first_label>=0 && (uint32_t)(hpos-hstart)>=labels[first_label].pos)@/ + { label_t *t=labels+first_label; + DBG(DBGLABEL,"Inserting label *%d\n", first_label); + hwrite_start(); + hwritef("label *%d",first_label); + if (t->where==LABEL_TOP) hwritef(" top"); + else if (t->where==LABEL_BOT) hwritef(" bot"); + nesting--;hwritec('>'); /* avoid a recursive call to |hwrite_end| */ + first_label=labels[first_label].next; + } +} +@ + +The short format specifies the label positions in the definition section. +This is not possible in the long format because there are no ``positions'' +in the long format. Therefore long format label nodes must +be inserted in the content section just before those nodes +that should come after the label. The function |hwrite_label| is called +in |hwrite_end|. At that point |hpos| is the position of the next node +and it can be compared with the positions of the labels taken from +the definition section. +Because |hpos| is strictly increasing while reading the content section, +the comparison can be made efficient by sorting the labels. +The sorting uses the |next| field in the +array of |labels| to construct a linked list. After sorting, the value of +|first_label| is the index of the label with the smallest position; +and for each |i|, the value of |labels[i].next| is the index of +the label with the next bigger position. If |labels[i].next| is negative, +there is no next bigger position. +Currently a simple insertion sort is used. +The insertion sort will work well if the labels are already +mostly in ascending order. +If we expect lots of labels in random order, +a more sophisticated sorting algorithm might be appropriate. + + + +@= +void hsort_labels(void) +{ int i; + if (max_ref[label_kind]<0) + { first_label=-1; return; @+} /* empty list */ + first_label=max_ref[label_kind]; + while (first_label>=0 && labels[first_label].where==LABEL_UNDEF) + first_label--; + if (first_label<0) return; /* no defined labels */ + labels[first_label].next=-1; + DBG(DBGLABEL,"Sorting %d labels\n",first_label+1); + for (i=first_label-1; i>=0; i--) /* insert label |i| */ + if (labels[i].where!=LABEL_UNDEF)@/ + { uint32_t pos=labels[i].pos; + if (labels[first_label].pos >= pos)@/ + { labels[i].next= first_label; first_label=i;@+ } /* new smallest */ + else @/ + { int j; + for (j= first_label; + labels[j].next>=0 && labels[labels[j].next].pos= +{ label_t *t; + int n; + if (i&b001) HGET16(n); @+else n=HGET8; + REF_RNG(label_kind,n); + t=labels+n; + if (t->where!=LABEL_UNDEF) + DBG(DBGLABEL,"Duplicate definition of label %d at 0x%x\n",n, node_pos); + HGET32(t->pos); + t->where=HGET8; + if (t->where==LABEL_UNDEF || t->where>LABEL_MID) + DBG(DBGLABEL,"Label %d where value invalid: %d at 0x%x\n",n,t->where,node_pos); + if (i&b010) /* secondary position */ + { HGET32(t->pos0); t->f=HGET8;@+} + else t->pos0=t->pos; + DBG(DBGLABEL,"Defining label %d at 0x%x\n",n,t->pos); +} +@ + + +The function |hput_label| is simply the reverse of the above code. + +\putcode +@= +uint8_t hput_label(int n, label_t *l) +{ info_t i=b000; + HPUTX(13); + if (n>0xFF) {i|=b001; HPUT16(n);@+}@+ else HPUT8(n); + HPUT32(l->pos); + HPUT8(l->where); + if (l->pos!=l->pos0) + { i|=b010; HPUT32(l->pos0); HPUT8(l->f); @+} + return TAG(label_kind,i); +} +@ + +|hput_label_defs| is called by the parser after the entire content +section has been processed; it appends the label definitions +to the definition section. +%Using the fact that the linked list +%starting at |first_label| already contains all labels in +%order of descending position, we could easily output the +%labels in sorted order and reconstruct the sorting while reading +%in the labels. The \HINT\ format however does not require +%label nodes to be sorted and the |hsort_labels| function +%can not be avoided. +The outlines are stored after the labels because they reference the labels. +@= +extern void hput_definitions_end(void); +extern uint8_t hput_outline(outline_t *t); +void hput_label_defs(void) +{ int n; + section_no=1; + hstart=dir[1].buffer; + hend=hstart+ dir[1].bsize; + hpos=hstart+dir[1].size;@/ + @@; + @@; + hput_definitions_end(); +} +@ + +@= + for (n=0; n<=max_ref[label_kind]; n++)@/ + { label_t *l=labels+n; + uint32_t pos; + if (l->used)@/ + { pos=hpos++-hstart; + hput_tags(pos,hput_label(n,l)); + if (l->where==LABEL_UNDEF) + MESSAGE("WARNING: Label *%d is used but not defined\n",n); + else + DBG(DBGDEF|DBGLABEL,"Label *%d defined 0x%x\n",n,pos);@/ + } + else + { if (l->where!=LABEL_UNDEF) + { pos=hpos++-hstart; + hput_tags(pos,hput_label(n,l)); + DBG(DBGDEF|DBGLABEL,"Label *%d defined but not used 0x%x\n",n,pos);@/ + } + } + } +@ + +Links are simpler than labels. They are found only in the +content section and resemble pretty much what we have seen for other +content nodes. Let's look at them next. +When reading a short format link node, +we use again the |b001| info bit to indicate a 16 bit reference +number to a label. The |b010| info bit indicates an ``on'' link. +\gdef\subcodetitle{Links} +\getcode +@= +#define @[HGET_LINK(I)@] @/\ +{ int n; if (I&b001) HGET16(n);@+ else n=HGET8; @+ hwrite_link(n,I&b010); @+} +@ + +@= +@t\1\kern1em@> +case TAG(link_kind,b000): @+ HGET_LINK(b000);@+ break; +case TAG(link_kind,b001): @+ HGET_LINK(b001);@+ break; +case TAG(link_kind,b010): @+ HGET_LINK(b010);@+ break; +case TAG(link_kind,b011): @+ HGET_LINK(b011);@+ break; +@ + +The function |hput_link| will insert the link in the output stream and return +the appropriate tag. + +\putcode +@= +uint8_t hput_link(int n, int on) +{ info_t i; + REF_RNG(label_kind,n); + labels[n].used=true; + if (on) i=b010;@+ else i=b000; + if (n>0xFF) { i|=b001; HPUT16(n);@+} @+else HPUT8(n); + return TAG(link_kind,i); +} +@ + +\readcode +@s LINK symbol +@= +%token LINK "link" +@ +@= +::@=link@> :< return LINK; >: +@ + +@= +content_node:start LINK REFERENCE on_off END + {@+ hput_tags($1,hput_link($3,$4));@+ }; +@ + +\writecode +@= +void hwrite_link(int n, uint8_t on) +{ REF_RNG(label_kind,n); + if (labels[n].where==LABEL_UNDEF) + MESSAGE("WARNING: Link to an undefined label %d\n",n); + hwrite_ref(n); + if (on) hwritef(" on"); + else hwritef(" off"); +} +@ + +Now we look at the +outline nodes which are found only in the definition section. +Every outline node is associated with a label node, giving the position in the +document, and a unique title that should tell the user +what to expect when navigating to this position. For example +an item with the title ``Table of Content'' should navigate +to the page that shows the table of content. +The sequence of outline nodes found in the definition section +gets a tree structure by assigning to each item a depth level. + +@= +typedef struct {@+ +uint8_t *t; /* title */ +int s; /* title size */ +int d; /* depth */ +uint16_t r; /* reference to a label */ +} outline_t; +@ + +@= +outline_t *outlines; +@ + +@= +if (max_outline>=0)@/ + ALLOCATE(outlines,max_outline+1,outline_t); +@ + +Child items follow their parent item and have a bigger depth level. +In the short format, the first item must be a root item, with +a depth level of 0. Further, if any item has the depth $d$, then the +item following it must have either the same depth $d$ in which +case it is a sibling, or the depth $d+1$ in which case it is a child, +or a depth $d^\prime$ with $0\le d^\prime= +n=0;@+ +while (n<=max_outline) + n=hcompress_depth(n,0); +@ +Outline items must be listed in the order +in which they should be displayed. +The function |hcompress_depth(n,c)| will compress the subtree starting at +|n| with root level |d| to a new tree with the same structure +and root level |c|. It returns the outline number of the +following subtree. + +@= +int hcompress_depth(int n, int c) +{ int d=outlines[n].d; + if (c>0xFF) + QUIT("Outline %d, depth level %d to %d out of range",n,d,c); + while (n<=max_outline) + if (outlines[n].d==d) + outlines[n++].d=c; + else if (outlines[n].d>d) + n=hcompress_depth(n,c+1); + else break; + return n; +} +@ + +For an outline node, the |b001| bit indicates a two byte reference to a label. +There is no reference number for an outline item itself: +it is never referenced anywhere in an \HINT\ file. + +\gdef\subcodetitle{Outlines} +\vbox{\getcode\vskip -\baselineskip\writecode} + +@= + { int r,d; + list_t l; + static int outline_no=-1; + hwrite_start();@+hwritef("outline"); + ++outline_no; + RNG("outline",outline_no, 0, max_outline); + if (i&b001) HGET16(r);@+ else r=HGET8; + REF_RNG(link_kind,r); + if (labels[r].where==LABEL_UNDEF)@/ + MESSAGE("WARNING: Outline with undefined label %d at 0x%x\n",@|r, node_pos); + hwritef(" *%d",r);@/ + d=HGET8; hwritef(" %d",d);@/ + hget_list(&l);hwrite_list(&l);@/ + hwrite_end(); + } +@ + +When parsing an outline definition in the long format, +we parse the outline title as a |list| which will +write the representation of the list to the output stream. +Writing the outline definitions, however, must be postponed +until the label have found their way into the definition +section. So we save the list's representation in the +outline node for later use and remove it again from the +output stream. + +\readcode +@s OUTLINE symbol + +@= +%token OUTLINE "outline" +@ + +@= +::@=outline@> :< return OUTLINE; >: +@ + +@= +def_node: START OUTLINE REFERENCE integer position list END { + static int outline_no=-1; + $$.k=outline_kind; $$.n=$3; + if ($6.s==0) QUIT("Outline with empty title in line %d",yylineno); + outline_no++; + hset_outline(outline_no,$3,$4,$5); + }; +@ + +@= +void hset_outline(int m, int r, int d, uint32_t pos) +{ outline_t *t; + RNG("Outline",m,0,max_outline); + t=outlines+m; + REF_RNG(label_kind,r); + t->r=r; + t->d=d; + t->s=hpos-(hstart+pos); + hpos=(hstart+pos); + ALLOCATE(t->t,t->s,uint8_t); + memmove(t->t,hpos,t->s); + labels[r].used=true; +} +@ +To output the title, we need to move the list back to the output stream. +Before doing so, we allocate space (and make sure there is room left for the +end tag of the outline node), and after doing so, we release +the memory used to save the title. + +@= + memmove(hpos,t->t,t->s); + hpos=hpos+t->s; + free(t->t); +@ + +We output all outline definitions from 0 to |max_outline| and +check that every one of them has a title. Thereby we make sure +that in the short format |max_outline| matches the number of +outline definitions. + +\putcode +@= +uint8_t hput_outline(outline_t *t) +{ info_t i=b100; + HPUTX(t->s+4); + if (t->r>0xFF) {i|=b001; @+HPUT16(t->r);@+} @+else HPUT8(t->r); + labels[t->r].used=true; + HPUT8(t->d); + @@; + return TAG(outline_kind,i); +} +@ + +@= +@@; +for (n=0;n<=max_outline;n++) +{ outline_t *t=outlines+n; + uint32_t pos; + pos=hpos++-hstart; + if (t->s==0 || t->t==NULL) + QUIT("Definition of outline %d has an empty title",n); + DBG(DBGDEF|DBGLABEL,"Outline *%d defined\n",n);@/ + hput_tags(pos,hput_outline(t)); +} +@ + +\subsection{Colors} +Colors\index{color} are certainly one of the features you will find in the final \HINT\ file format. +Here some remarks must suffice. + +A \HINT\ viewer must be capable of rendering a page given just any valid +position inside the content section. Therefore \HINT\ files are stateless; +there is no need to search for preceding commands that might change a state +variable. +As a consequence, we can not just define a ``color change node''. +Colors could be specified as an optional parameter of a glyph node, but the +amount of data necessary would be considerable. In texts, on the other hand, +a color change control code would be possible because we parse texts only in forward +direction. The current font would then become a current color and font with the appropriate +changes for positions. + +A more attractive alternative would be to specify colored fonts. +This would require an optional +color argument for a font. For example one could have a cmr10 font in black as +font number 3, and a cmr10 font in blue as font number 4. Having 256 different fonts, +this is definitely a possibility because rarely you would need that many fonts +or that many colors. If necessary and desired, one could allow 16 bit font numbers +of overcome the problem. + +Background colors could be associated with boxes as an optional parameter. + + +\section{Replacing \TeX's Page Building Process} + +\TeX\ uses an output\index{output routine} routine to finalize the page. It uses the accumulated material +from the page builder, found in {\tt box255}, attaches headers, footers, and floating material +like figures, tables, and footnotes. The latter material is specified by insert nodes +while headers and footers are often constructed using mark nodes. +Running an output routine requires the full power of the \TeX\ engine and will not be +part of the \HINT\ viewer. Therefore, \HINT\ replaces output routines by page templates\index{template}. +As \TeX\ can use different output routines for different parts of a book---for example +the index might use a different output routine than the main body of text---\HINT\ +will allow multiple page templates. To support different output media, the page +templates will be named and a suitable user interface may offer the user a selection +of possible page layouts. In this way, the page layout remains in the hands of the +book designer, and the user has still the opportunity to pick a layout that best fits +the display device. + +\TeX\ uses insertions to describe floating content that is not necessarily displayed +where it is specified. Three examples may illustrate this: +\itemize +\item Footnotes\footnote*{Like this one.} are specified in the middle of the text but are displayed at the +bottom of the page. Several +footnotes\index{footnote} on the same page are collected and displayed together. The +page layout may specify a short rule to separate footnotes from the +main text, and if there are many short footnotes, it may use two columns +to display them. In extreme cases, the page layout may demand a long +footnote to be split and continued on the next page. + +\item Illustrations\index{illustration} may be displayed exactly where specified if there is enough +room on the page, but may move to the top of the page, the bottom of the page, +the top of next page, or a separate page at the end of the chapter. + +\item Margin notes\index{margin note} are displayed in the margin on the same page starting at the top +of the margin. +\enditemize + +\HINT\ uses page templates and content streams to achieve similar effects. +But before I describe the page building\index{page building} mechanisms of \HINT, let me summarize \TeX's page builder. + +\TeX's page builder ignores leading glue\index{glue}, kern\index{kern}, and penalty\index{penalty} nodes until the first +box\index{box} or rule\index{rule} is encountered; +whatsit\index{whatsit node} nodes do not really contribute anything to a page; mark\index{mark node} nodes are recorded for later use. +Once the first box, rule, or insert\index{insert node} arrives, \TeX\ makes copies of all parameters +that influence the page building process and uses these copies. These parameters +are the |page_goal| and the |page_max_depth|. Further, the variables +|page_total|, |page_shrink|, |page_stretch|, |page_depth|, +and {\it insert\_pe\-nal\-ties\/} are initialized to zero. +The top skip\index{top skip} adjustment is made +when the first box or rule arrives---possibly after an insert. + +Now the page builder accumulates material: normal material goes into {\tt box255}\index{box 255} and will change |page_total|, |page_shrink|, +|page_stretch|, and |page_depth|. The latter is adjusted so that +is does not exceed |page_max_depth|. + +The handling of inserts\index{insert node} is more complex. +\TeX\ creates an insert class using \.{newinsert}. This reserves a number $n$ +and four registers: {\tt box\hair$n$} for the inserted material, {\tt count\hair$n$} for the +magnification factor $f$, {\tt dimen\hair$n$} for the maximum size per page $d$, and {\tt skip\hair$n$} for the +extra space needed on a page if there are any insertions of class $n$. + +For example plain \TeX\ allocates $n=254$ for footnotes\index{footnote} and sets +{\tt count254} to~$1000$, {\tt dimen254} to 8in, and {\tt skip254} to {\tt \BS bigskipamount}. + +An insertion node will specify the insertion class $n$, some vertical material, +its natural height plus depth $x$, a {\it split\-\_top\-\_skip}, a {\it split\-\_max\_depth}, +and a {\it floa\-ting\-\_pe\-nal\-ty}. + + +Now assume that an insert node with subtype 254 arrives at the page builder. +If this is the first such insert, \TeX\ will decrease the |page_goal| +by the width of skip254 and adds its stretchability and shrinkability +to the total stretchability and shrinkability of the page. Later, +the output routine will add some space and the footnote rule to fill just that +much space and add just that much shrinkability and stretchability to the page. +Then \TeX\ will normally add the vertical material in the insert node to +box254 and decrease the |page_goal| by $x\times f/1000$. + +Special processing is required if \TeX\ detects that there is not enough space on +the current page to accommodate the complete insertion. +If already a previous insert did not fit on the page, simply the |floating_penalty| +as given in the insert node is added to the total |insert_penalties|. +Otherwise \TeX\ will test that the total natural height plus depth of box254 +including $x$ does not exceed the maximum size $d$ and that the +$|page_total| + |page_depth| + x\times f/1000 - |page_shrink| \le |page_goal|$. +If one of these tests fails, the current insertion +is split in such a way as to make the size of the remaining insertions just pass the tests +just stated. + +Whenever a glue node, or penalty node, or a kern node that is followed by glue arrives +at the page builder, it rates the current position as a possible end of the page based on +the shrinkability of the page and the difference between |page_total| and |page_goal|. +As the page fills, the page breaks tend to become better and better until the +page starts to get overfull and the page breaks get worse and worse until +they reach the point where they become |awful_bad|. At that point, +the page builder returns to the best page break found so far and fires up the +output routine. + +Let's look next at the problems that show up when implementing a replacement mechanism for \HINT. + +\enumerate +\item +An insertion node can not always specify its height $x$ because insertions may contain paragraphs that need +to be broken in lines and the height of a paragraph depends in some non obvious way on +its width. + +\item +Before the viewer can compute the height $x$, it needs to know the width of the insertion. Just imagine +displaying footnotes in two columns or setting notes in the margin. Knowing the width, it +can pack the vertical material and derive its height and depth. + +\item +\TeX's plain format provides an insert macro that checks whether there is still space +on the current page, and if so, it creates a contribution to the main text body, otherwise it +creates a topinsert. Such a decision needs to be postponed to the \HINT\ viewer. + +\item +\HINT\ has no output routines that would specify something like the space and the rule preceding the footnote. + +\item +\TeX's output routines have the ability to inspect the content of the boxes, +split them, and distribute the content over the page. +For example, the output routine for an index set in two column format might +expect a box containing index entries up to a height of $2\times\.{vsize}$. +It will split this box in the middle and display the top part in the left +column and the bottom part in the right column. With this approach, the +last page will show two partly filled columns of about equal size. + +\item +\HINT\ has no mark nodes that could be used to create page headers or footers. +Marks, like output routines, contain token lists and need the full \TeX\ interpreter +for processing them. Hence, \HINT\ does not support mark nodes. +\endenumerate + +Here now is the solution I have chosen for \HINT: + +Instead of output routines, \HINT\ will use page templates. +Page templates are basically vertical boxes with placeholders marking the +positions where the content of the box registers, filled by the page builder, +should appear. +To output the page, the viewer traverses the page template, +replaces the placeholders by the appropriate box content, and +sets the glue. Inside the page template, we can use insert nodes to act +as placeholders. + +It is only natural to treat the page's main body, the +inserts, and the marks using the same mechanism. We call this +mechanism a content stream\index{stream}. +Content streams are identified by a stream number in the range 0 to 254; +the number 255 is used to indicate an invalid stream number. +The stream number 0 is reserved for the main content stream; it is always defined. +Besides the main content stream, there are three types of streams: +\itemize +\item normal streams correspond to \TeX's inserts and accumulate content on the page, +\item first\index{first stream} streams correspond to \TeX's first marks and will contain only the first insertion of the page, +\item last\index{last stream} streams correspond to \TeX's bottom marks and will contain only the last insertion of the page, and +\item top\index{top stream} streams correspond to \TeX's top marks. Top streams are not yet implemented. +\enditemize + +Nodes from the content section are considered contributions to stream 0 except +for insert nodes which will specify the stream number explicitly. +If the stream is not defined or is not used in the current page template, its content is simply ignored. + +The page builder needs a mechanism to redirect contributions from one content +stream to another content stream based on the availability of space. +Hence a \HINT\ content stream can optionally specify a preferred stream number, +where content should go if there is still space available, a next stream number, +where content should go if the present stream has no more space available, and +a split ratio if the content is to be split between these two streams before +filling in the template. + +Various stream parameters govern the treatment of contributions to the stream +and the page building process. + +\itemize +\item The magnification factor $f$: Inserting a box of height $h$ to this stream will contribute $h\times f/1000$ +to the height of the page under construction. For example, a stream +that uses a two column format will have an $f$ value of 500; a stream +that specifies notes that will be displayed in the page margin will +have an $f$ value of zero. + +\item The height $h$: The extended dimension $h$ gives the maximum height this +stream is allowed to occupy on the current page. +To continue the previous example, a stream that will be split into two columns +will have $h=2\cdot\.{vsize}$ , and a stream that specifies +notes that will be displayed in the page margin will have +$h=1\cdot\.{vsize}$. You can restrict the amount of space occupied by +footnotes to the bottom quarter by setting the corresponding $h$ value +to $h=0.25\cdot\.{vsize}$. + +\item The depth $d$: The dimension $d$ gives the maximum depth this +stream is allowed to have after formatting. + +\item The width $w$: The extended dimension $w$ gives the width of this stream +when formatting its content. For example margin notes +should have the width of the margin less some surrounding space. + +\item The ``before'' list $b$: If there are any contributions to this +stream on the current page, the material in list $b$ +is inserted {\it before\/} the material from the stream itself. For +example, the short line that separates the footnotes from the main +page will go, together with some surrounding space, into the list~$b$. + +\item The top skip glue $g$: This glue is inserted between the material +from list $b$ and the first box of the stream, reduced +by the height of the first box. Hence it specifies the distance between +the material in $b$ and the first baseline of the stream content. + +\item The ``after'' list $a$: The list $a$ is treated like list $b$ but +its material is placed {\it after\/} the material from the stream itself. + +\item The ``preferred'' stream number $p$: If $p\ne 255$, it is the number of +the {\it preferred\/} stream. If stream $p$ has still +enough room to accommodate the current contribution, move the +contribution to stream $p$, otherwise keep it. For example, you can +move an illustration to the main content stream, provided there is +still enough space for it on the current page, by setting $p=0$. + +\item The ``next'' stream number $n$: If $n\ne 255$, it is the number of the +{\it next\/} stream. If a contribution can not be +accommodated in stream $p$ nor in the current stream, treat it as an +insertion to stream $n$. For example, you can move contributions to +the next column after the first column is full, or move illustrations +to a separate page at the end of the chapter. + +\item The split ratio\index{split ratio} $r$: If $r$ is positive, both $p$ and $n$ must +be valid stream numbers and contents is not immediately moved to stream $p$ or $n$ as described before. +Instead the content is kept in the stream itself until the current page is complete. +Then, before inserting the streams into the page template, the content of +this stream is formatted as a vertical box, the vertical box is +split into a top fraction and a bottom fraction in the ratio $r/1000$ +for the top and $(1000-r)/1000$ for the bottom, and finally the top +fraction is moved to stream $p$ and the bottom fraction to stream +$n$. You can use this feature for example to implement footnotes +arranged in two columns of about equal size. By collecting all the +footnotes in one stream and then splitting the footnotes with $r=500$ +before placing them on the page into a right and left column. Even +three or more columns can be implemented by cascades of streams using +this mechanism. +\enditemize + +\subsection{Stream Definitions} +\index{stream} +There are four types of streams: normal streams that work like \TeX's inserts; +and first, last, and top streams that work like \TeX's marks. +For the latter types, the long format uses a matching keyword and the +short format the two least significant info bits. All stream definitions +start with the stream number. +In definitions of normal streams after the number follows in this order +\itemize +\item the maximum insertion height, +\item the magnification factor, and +\item information about splitting the stream. + It consists of: a preferred stream, a next stream, and a split ratio. + An asterisk indicates a missing stream reference, in the + short format the stream number 255 serves the same purpose. +\enditemize +All stream definitions finish with +\itemize +\item the ``before'' list, +\item an extended dimension node specifying the width of the inserted material, +\item the top skip glue, +\item the ``after'' list, +\item and the total height, stretchability, and shrinkability of the material in + the ``before'' and ``after'' list. +\enditemize + +A special case is the stream definition for stream 0, the main content stream. +None of the above information is necessary for it so it is omitted. +Stream definitions, including the definition of stream 0, +occur only inside page template definitions\index{template} +where they occur twice in two different roles: +In the stream definition list, they define properties of the stream +and in the template they mark the insertion point (see section~\secref{page}). +In the latter case, stream nodes just contain the stream number. +Because a template looks like ordinary vertical material, +we like to use the same functions for parsing it. +But stream definitions are very different from stream content +nodes. To solve the problem for the long format, +the scanner will return two different tokens +when it sees the keyword ``{\tt stream}''. +In the definition section, it will return +|STREAMDEF| and in the content section |STREAM|. +The same problem is solved in the short format +by using the |b100| bit to mark a definition. + +\goodbreak +\vbox{\readcode\vskip -\baselineskip\putcode} + +@s STREAM symbol +@s STREAMDEF symbol +@s TOP symbol +@s FIRST symbol +@s LAST symbol +@s NOREFERENCE symbol +@s stream_type symbol +@s stream_info symbol +@s stream_split symbol +@s stream_link symbol +@s stream_def_node symbol +@s stream_ins_node symbol +@s stream_ref symbol + + +@= +%token STREAM "stream" +%token STREAMDEF "stream (definition)" +%token FIRST "first" +%token LAST "last" +%token TOP "top" +%token NOREFERENCE "*" +%type stream_type +%type stream_ref +%type stream_def_node +@ + +@= +::@=stream@> :< if (section_no==1) return STREAMDEF; else return STREAM;@+ >: +::@=first@> :< return FIRST; >: +::@=last@> :< return LAST; >: +::@=top@> :< return TOP; >: +::@=\*@> :< return NOREFERENCE; >: +@ + +@= +stream_link: ref { REF_RNG(stream_kind,$1); } | NOREFERENCE {HPUT8(255);}; +stream_split: stream_link stream_link UNSIGNED @/{RNG("split ratio",$3,0,1000); HPUT16($3);}; +stream_info: xdimen_node UNSIGNED @/{RNG("magnification factor",$2,0,1000); HPUT16($2);} stream_split; + +stream_type: stream_info {$$=0;} |FIRST {$$=1;} @+ | LAST {$$=2;} @+ |TOP {$$=3;} ; + +stream_def_node: start STREAMDEF ref stream_type @/ + list xdimen_node glue_node list glue_node END @/ + {@+ DEF($$,stream_kind,$3); @+ hput_tags($1,TAG(stream_kind,$4|b100));}; + +stream_ins_node: start STREAMDEF ref END@/ + { RNG("Stream insertion",$3,0,max_ref[stream_kind]); hput_tags($1,TAG(stream_kind,b100));}; + +content_node: stream_def_node @+ | stream_ins_node; +@ + + +\goodbreak +\vbox{\getcode\vskip -\baselineskip\writecode} + + + +@= +{ xdimen_t x; + uint16_t f,r; + uint8_t n; + DBG(DBGDEF,"Defining normal stream %d at " SIZE_F "\n",*(hpos-1),hpos-hstart-2); + hget_xdimen_node(&x); @+hwrite_xdimen_node(&x); + HGET16(f); @+RNG("magnification factor",f,0,1000);@+ hwritef(" %d",f); + n=HGET8; if (n==255) hwritef(" *"); else { REF_RNG(stream_kind,n);@+hwrite_ref(n);@+} + n=HGET8; if (n==255) hwritef(" *"); else { REF_RNG(stream_kind,n);@+hwrite_ref(n);@+} + HGET16(r); RNG("split ratio",r,0,1000); hwritef(" %d",r); +} +@ + +@= +static bool hget_stream_def(void) +{@+ if (KIND(*hpos)!=stream_kind || !(INFO(*hpos)&b100)) + return false; + else + { ref_t df; + @@; + DBG(DBGDEF,"Defining stream %d at " SIZE_F "\n",*hpos,hpos-hstart-1); + DEF(df,stream_kind,HGET8); + hwrite_start();@+hwritef("stream");@+@+hwrite_ref(df.n); + if (df.n>0) + { xdimen_t x; @+ list_t l; + if (INFO(a)==b100) @@; + else if (INFO(a)==b101) hwritef(" first"); + else if(INFO(a)==b110) hwritef(" last"); + else if (INFO(a)==b111) hwritef(" top"); + hget_list(&l);@+ hwrite_list(&l); + hget_xdimen_node(&x); @+hwrite_xdimen_node(&x); + hget_glue_node();@+ + hget_list(&l);@+ hwrite_list(&l);@+ + hget_glue_node(); + } + @@; + hwrite_end(); + return true; + } +} + +@ + +When stream definitions are part of the page template, we call them +stream insertion points. +They contain only the stream reference and +are parsed by the usual content parsing functions. + +@= +@t\1\kern1em@> +case TAG(stream_kind,b100): {uint8_t n=HGET8;@+ REF_RNG(stream_kind,n); @+hwrite_ref(n); @+ break; @+} +@ + + +\subsection{Stream Content} +Stream\index{stream} nodes occur in the content section where they +must not be inside other nodes except toplevel +paragraph\index{paragraph} nodes. A normal stream node contains in this +order: the stream reference number, the optional stream parameters, +and the stream content. The content is either a vertical box or an +extended vertical box. The stream parameters consists of the +|floating_penalty|, the |split_max_depth|, and the +|split_top_skip|. The parameterlist can be given +explicitly or as a reference. + +In the short format, the info bits |b010| indicate +a normal stream content node with an explicit parameter list +and the info bits |b000| a normal stream with a parameter list reference. + +If the info bit |b001| is set, we have a content node of type top, first, +or last. In this case, the short format has instead of the parameter list +a single byte indicating the type. +These types are currently not yet implemented. + +\goodbreak +\vbox{\readcode\vskip -\baselineskip\putcode} + +@s stream symbol + +@= +%type stream +@ + +@= +stream: empty_param_list list {$$=b010;} + | empty_param_list non_empty_param_list list {$$=b010;} + | param_ref list {$$=b000;}; +content_node: start STREAM stream_ref stream END + @/{@+hput_tags($1,TAG(stream_kind,$4)); @+}; +@ + +\goodbreak +\vbox{\getcode\vskip -\baselineskip\writecode} + +@= +@t\1\kern1em@> +case TAG(stream_kind,b000): HGET_STREAM(b000); @+ break; +case TAG(stream_kind,b010): HGET_STREAM(b010); @+ break; +@ + +When we read stream numbers, we relax the define before use policy. +We just check, that the stream number is in the correct range. +\goodbreak +@= +#define @[HGET_STREAM(I)@] @/\ + {uint8_t n=HGET8;@+ REF_RNG(stream_kind,n); @+hwrite_ref(n);@+}\ +if ((I)&b010) { list_t l; @+hget_param_list(&l); @+hwrite_param_list(&l); @+} \ +else HGET_REF(param_kind);\ +{ list_t l; @+hget_list(&l);@+ hwrite_list(&l); @+} +@ + + + + +\subsection{Page Template Definitions}\label{page} +A \HINT\ file can define multiple page templates\index{template}. Not only +might an index demand a different page layout than the main body of text, +also the front page or the chapter headings might use their own page templates. +Further, the author of a \HINT\ file might define a two column format as +an alternative to a single column format to be used if the display area +is wide enough. + +To help in selecting the right page template, page template definitions start with +a name and an optional priority\index{priority}; the default priority is 1. +The names might appear in a menu from which the user +can select a page layout that best fits her taste. +Without user interaction, the +system can pick the template with the highest priority. Of course, +a user interface might provide means to alter priorities. Future +versions might include sophisticated feature-vectors that +identify templates that are good for large or small displays, +landscape or portrait mode, etc \dots + +After the priority follows a glue node to specify the topskip glue +and the dimension of the maximum page depth, +an extended dimension to specify the page height and +an extended dimension to specify the page width. + +Then follows the main part of a page template definition: the template. +The template consists of a list of vertical material. +To construct the page, this list will be placed +into a vertical box and the glue will be set. +But of course before doing so, the viewer will +scan the list and replace all stream insertion points +by the appropriate content streams. + +Let's call the vertical box obtained this way ``the page''. +The page will fill the entire display area top to bottom and left to right. +It defines not only the appearance of the main body of text +but also the margins, the header, and the footer. +Because the \.{vsize} and \.{hsize} variables of \TeX\ are used for +the vertical and horizontal dimension of the main body of text---they +do not include the margins---the page will usually be wider than \.{hsize} +and taller than \.{vsize}. The dimensions of the page are part +of the page template. The viewer, knowing the actual dimensions +of the display area, can derive from them the actual values of \.{hsize} +and \.{vsize}. + +Stream definitions are listed after the template. + +The page template with number 0 is always defined and has priority 0. +It will display just the main content stream. It puts a small margin +of $\.{hsize}/8 -4.5\hbox{pt}$ all around it. +Given a letter size page, 8.5 inch wide, this formula yields a margin of 1 inch, +matching \TeX's plain format. The margin will be positive as long as +the page is wider than $1/2$ inch. For narrower pages, there will be no +margin at all. In general, the \HINT\ viewer will never set {\tt hsize} larger +than the width of the page and {\tt vsize} larger than its height. + +%8.5 in should give 1 inch margin 2/17 +%612pt should give 72pt margin +%72pt = 612/8-4.5pt +%This would give a positive margin starting at 36pt or 1/2 inch + +\goodbreak +\vbox{\readcode\vskip -\baselineskip\putcode} + +@s PAGE symbol +@s page_priority symbol +@s page symbol +@s stream_def_list symbol + +@= +%token PAGE "page" +@ + +@= +::@=page@> :< return PAGE; >: +@ + +@= +page_priority: { HPUT8(1); } + | UNSIGNED { RNG("page priority",$1,0,255); HPUT8($1); }; + +stream_def_list: | stream_def_list stream_def_node; + +page: string { hput_string($1);} page_priority glue_node dimension {@+HPUT32($5);@+} + xdimen_node xdimen_node + list stream_def_list ; +@ + +\goodbreak +\vbox{\getcode\vskip -\baselineskip\writecode} +@= +void hget_page(void) +{ char *n; uint8_t p; xdimen_t x; list_t l; + HGET_STRING(n);@+ hwrite_string(n); + p=HGET8; @+ if (p!=1) hwritef(" %d",p); + hget_glue_node(); + hget_dimen(TAG(dimen_kind,b001)); + hget_xdimen_node(&x); @+hwrite_xdimen_node(&x); /* page height */ + hget_xdimen_node(&x); @+hwrite_xdimen_node(&x); /* page width */ + hget_list(&l);@+ hwrite_list(&l); + while (hget_stream_def()) continue; +} +@ + +\subsection{Page Ranges}\label{range}\index{page range} +Not every template\index{template} is necessarily valid for the entire content +section. A page range specifies a start position $a$ and an end +position $b$ in the content section and the page template is valid if +the start position $p$ of the page is within that range: $a\le p < b$. +If paging backward this definition might cause problems because the +start position of the page is known only after the page has been +build. In this case, the viewer might choose a page template based on +the position at the bottom of the page. If it turns out that this ``bottom template'' +is no longer valid when the page builder has found the start of the +page, the viewer might display the page anyway with the bottom +template, it might just display the page with the new ``top +template'', or rerun the whole page building process using this time +the ``top template''. Neither of these alternatives is guaranteed to +produce a perfect result because changing the page template might +change the amount of material that fits on the page. A good page +template design should take this into account. + +The representation of page ranges differs significantly for the short +format and the long format. The short format will include a list of page +ranges in the definition section which consist of a page template number, +a start position, and an end position. In the long format, the start +and end position of a page +range is marked with a page range node switching the availability of a +page template on and off. Such a page range node must be a top level node. +It is an error, to switch a page template +off that was not switched on, or to switch a page template on that was +already switched on. It is permissible to omit switching off a page +template at the very end of the content section. + +While we parse a long format \HINT\ file, we store page ranges and generate +the short format after reaching the end of the content section. +While we parse a short format \HINT\ file, +we check at the end of each top level node whether we should insert a +page range node into the output. +For the \.{shrink} program, it is best +to store the start and end positions of all page ranges +in an array sorted by the position\footnote*{For a \HINT\ viewer, +a data structure which allows fast retrieval of all +valid page templates for a given position is needed.}. +To check the restrictions on the switching of page templates, we +maintain for every page template an index into the range array +which identifies the position where the template was switched on. +A zero value instead of an index will identify templates that +are currently invalid. When switching a range off again, we +link the two array entries using this index. These links +are useful when producing the range nodes in short format. + +A range node in short format contains the template number, the +start position and the end position. +A zero start position +is not stored, the info bit |b100| indicates a nonzero start position. +An end position equal to |HINT_NO_POS| is not stored, +the info bit |b010| indicates a smaller end position. +The info bit |b001| indicates that positions are stored using 2 byte +otherwise 4 byte are used for the positions. + +@= +typedef +struct {@+uint8_t pg; @+uint32_t pos; @+ bool on; @+int link;@+} range_pos_t; +@ + +@= +range_pos_t *range_pos; +int next_range=1, max_range; +int *page_on; +@ + +@= +ALLOCATE(page_on,max_ref[page_kind]+1,int); +ALLOCATE(range_pos,2*(max_ref[range_kind]+1),range_pos_t); +@ + +@= +#define @[ALLOCATE(R,S,T)@] @/((R)=@[(T *)calloc((S),sizeof(T)),\ + (((R)==NULL)?QUIT("Out of memory for " #R):0)) +#define @[REALLOCATE(R,S,T)@] @/((R)=@[(T *)realloc((R),(S)*sizeof(T)),\ + (((R)==NULL)?QUIT("Out of memory for " #R):0)) +@ + +\readcode +@s RANGE symbol +@= +%token RANGE "range" +@ + +@= +::@=range@> :< return RANGE; >: +@ +@= + +content_node: START RANGE REFERENCE ON END @/{ REF(page_kind,$3);hput_range($3,true);} + | START RANGE REFERENCE OFF END @/{ REF(page_kind,$3);hput_range($3,false);}; +@ + + +\writecode +@= +void hwrite_range(void) /* called in |hwrite_end| */ +{ uint32_t p=hpos-hstart; + DBG(DBGRANGE,"Range check at pos 0x%x next at 0x%x\n",p,range_pos[next_range].pos); + while (next_range'); /* avoid a recursive call to |hwrite_end| */ + next_range++; + } +} +@ + +\getcode +@= +void hget_range(info_t info, uint8_t pg) +{ uint32_t from, to; + REF(page_kind,pg); + REF(range_kind,(next_range-1)/2); + if (info&b100) @+ + { @+ if (info&b001) HGET32(from); @+else HGET16(from); @+} + else from=0; + if (info&b010) @+ + { @+if (info&b001) HGET32(to); @+else HGET16(to); @+} + else to=HINT_NO_POS; + range_pos[next_range].pg=pg; + range_pos[next_range].on=true; + range_pos[next_range].pos=from; + DBG(DBGRANGE,"Range *%d from 0x%x\n",pg,from); + DBG(DBGRANGE,"Range *%d to 0x%x\n",pg,to); + next_range++; + if (to!=HINT_NO_POS) @/ + { range_pos[next_range].pg=pg; + range_pos[next_range].on=false; + range_pos[next_range].pos=to; + next_range++; + } +} +@ + +@= +void hsort_ranges(void) /* simple insert sort by position */ +{ int i; + DBG(DBGRANGE,"Range sorting %d positions\n",next_range-1); + for(i=3; i= +void hput_range(uint8_t pg, bool on) +{ if (((next_range-1)/2)>max_ref[range_kind]) + QUIT("Page range %d > %d",(next_range-1)/2,max_ref[range_kind]); + if (on && page_on[pg]!=0) + QUIT(@["Template %d is switched on at 0x%x and " SIZE_F@],@| + pg, range_pos[page_on[pg]].pos, hpos-hstart); + else if (!on && page_on[pg]==0) + QUIT(@["Template %d is switched off at " SIZE_F " but was not on"@],@| + pg, hpos-hstart); + DBG(DBGRANGE,@["Range *%d %s at " SIZE_F "\n"@],pg,on?"on":"off",hpos-hstart); + range_pos[next_range].pg=pg; + range_pos[next_range].pos=hpos-hstart; + range_pos[next_range].on=on; + if (on) page_on[pg]=next_range; + else @/{ range_pos[next_range].link =page_on[pg]; + range_pos[page_on[pg]].link=next_range; + page_on[pg]=0; } + next_range++; +} + +void hput_range_defs(void) +{ int i; + section_no=1; + hstart=dir[1].buffer; + hend=hstart+ dir[1].bsize; + hpos=hstart+dir[1].size; + for (i=1; i< next_range;i++) + if (range_pos[i].on)@/ + { info_t info=b000; + uint32_t p=hpos++-hstart; + uint32_t from, to; + HPUT8(range_pos[i].pg); + from= range_pos[i].pos; + if (range_pos[i].link!=0) to = range_pos[range_pos[i].link].pos; + else to=HINT_NO_POS; + if (from!=0) @/ + { info=info|b100;@+ if (from>0xFFFF) @+ info=info|b001;@+} + if (to!=HINT_NO_POS) @/ + { info=info|b010;@+ if (to>0xFFFF) info=info|b001;@+ } + if (info & b100) @/ + { @+if (info & b001) HPUT32(from); @+else HPUT16(from); @+} + if (info & b010) @/ + { @+if (info & b001) HPUT32(to); @+else HPUT16(to); @+} + DBG(DBGRANGE,"Range *%d from 0x%x to 0x%x\n",@|range_pos[i].pg,from, to); + hput_tags(p,TAG(range_kind,info)); + } + hput_definitions_end(); + } +@ + + +\section{File Structure}\hascode +All \HINT\ files\index{file} start with a banner\index{banner} as +described below. After that, they contain three mandatory +sections\index{section}: the directory\index{directory section} +section, the definition\index{definition section} section, and the +content\index{content section} section. Usually, further +optional\index{optional section} sections follow. In short format +files, these contain auxiliary\index{auxiliary file} files +(fonts\index{font}, images\index{image}, \dots) necessary for +rendering the content. In long format files, the directory section +will simply list the file names of the auxiliary files. + + + +\subsection{Banner} +All \HINT\ files start with a banner\index{banner}. The banner contains only +printable ASCII characters and spaces; +its end is marked with a newline character\index{newline character}. +The first four byte are the ``magic'' number by which you recognize a \HINT\ +file. It consists of the four ASCII codes `{\tt H}', `{\tt I}', `{\tt N}', +and `{\tt T}' in the long format and `{\tt h}', `{\tt i}', `{\tt n}', +and `{\tt t}' in the short format. Then follows a space, then +the version number, a dot, the sub-version number, and another +space. Both numbers are encoded as decimal ASCII strings. The +remainder of the banner is simply ignored but may be used to contain +other useful information about the file. The maximum size of the +banner is 256 byte. +@= +#define HINT_VERSION 1 +#define HINT_SUB_VERSION 3 +#define MAX_BANNER 256 +@ + +\goodbreak +To check the banner, we have the function |hcheck_banner|; +it returns |true| if successful. + +@= +char hbanner[MAX_BANNER+1]; +@ + +@= + +bool hcheck_banner(char *magic) +{ int hbanner_size=0; + int v; + char *t; + t=hbanner; + if (strncmp(magic,hbanner,4)!=0) + { MESSAGE("This is not a %s file",magic); return false; } + else t+=4; + hbanner_size=(int)strnlen(hbanner,MAX_BANNER); + if(hbanner[hbanner_size-1]!='\n') + { MESSAGE("Banner exceeds maximum size=0x%x",MAX_BANNER); return false; } + if (*t!=' ') + { MESSAGE("Space expected in banner after %s",magic); return false; } + else t++; + v=strtol(t,&t,10); + if (v!=HINT_VERSION) + { MESSAGE("Wrong HINT version: got %d, expected %d",v,HINT_VERSION); return false; } + if (*t!='.') + { MESSAGE("Dot expected in banner after HINT version number"); return false; } + else t++; + v=strtol(t,&t,10); + if (v!=HINT_SUB_VERSION) + { MESSAGE("Wrong HINT subversion: got %d, expected %d",v,HINT_SUB_VERSION); return false; } + if (*t!=' ' && *t!='\n') + { MESSAGE("Space expected in banner after HINT subversion"); return false; } + LOG("%s file version %d.%d:%s",magic,HINT_VERSION, HINT_SUB_VERSION, t); + DBG(DBGDIR,"banner size=0x%x\n",hbanner_size); + return true; +} +@ + +To read a short format file, we use the macro |HGET8|. It returns a single byte. +We read the banner knowing that it ends with a newline character +and is at most |MAX_BANNER| byte long. Because this is the first access to a yet unknown file, +we are very careful and make sure we do not read past the end of the file. +Checking the banner is a separate step. + +\getcode +@= +void hget_banner(void) +{ int i; + for (i=0;i= +{ int i,c; + for (i=0;i= + +static size_t hput_banner(char *magic, char *s) +{ return fprintf(hout,"%s %d.%d %s\n",magic,HINT_VERSION,HINT_SUB_VERSION,s); +} +@ + + +\writecode +Writing the banner of a long format file is essentially the same as for a short +format file calling |hput_banner| with |"HINT"| as a first argument. + +\subsection{Long Format Files}\gdef\subcodetitle{Banner}% + +After reading and checking the banner, reading a long format file is +simply done by calling |yyparse|. The following rule gives the big picture: +\readcode +@s hint symbol +@s content_section symbol + +@= +hint: directory_section definition_section content_section ; +@ + + +\subsection{Short Format Files}\gdef\subcodetitle{Primitives}% +A short format\index{short format} file starts with the banner and continues +with a list of sections. Each section has a maximum size +of $2^{32}$ byte or 4GByte. This restriction ensures that positions\index{position} +inside a section can be stored as 32 bit integers, a feature that +we will need only for the so called ``content'' section, but it +is also nice for implementers to know in advance what sizes to expect. +The big picture is captured by the |put_hint| function: + +@= +static size_t hput_root(void); +static size_t hput_section(uint16_t n); +static void hput_optional_sections(void); + +void hput_hint(char * str) +{ size_t s; + DBG(DBGBASIC,"Writing hint output %s\n",str); + s=hput_banner("hint",str); + DBG(DBGDIR,@["Root entry at " SIZE_F "\n"@],s); + s+=hput_root(); + DBG(DBGDIR,@["Directory section at " SIZE_F "\n"@],s); + s+=hput_section(0); + DBG(DBGDIR,@["Definition section at " SIZE_F "\n"@],s); + s+=hput_section(1); + DBG(DBGDIR,@["Content section at " SIZE_F "\n"@],s); + s+=hput_section(2); + DBG(DBGDIR,@["Auxiliary sections at " SIZE_F "\n"@],s); + hput_optional_sections(); +} +@ + + +When we work on a section, we will have the entire section in +memory and use three variables to access it: |hstart| +points to the first byte of the section, |hend| points +to the byte after the last byte of the section, and |hpos| points to the +current position inside the section.\label{hpos} +The auxiliary variable |hpos0| contains the |hpos| value of the +last content node on nesting level zero. + +@= +uint8_t *hpos=NULL, *hstart=NULL, *hend=NULL, *hpos0=NULL; +@ + +There are two sets of macros that read or write binary data at the current position +and advance the stream position accordingly.\label{HPUT}\label{HGET} + +\getcode +@= +#define HGET_ERROR @/ QUIT(@["HGET overrun in section %d at " SIZE_F "\n"@],@|section_no,hpos-hstart) +#define @[HEND@] @[((hpos<=hend)?0:(HGET_ERROR,0))@] + +#define @[HGET8@] ((hpos= +void hput_error(void) +{@+if (hpos= +extern void hput_error(void); +#define @[HPUT8(X)@] (hput_error(),*(hpos++)=(X)) +#define @[HPUT16(X)@] (HPUT8(((X)>>8)&0xFF),HPUT8((X)&0xFF)) +#define @[HPUT24(X)@] (HPUT8(((X)>>16)&0xFF),HPUT8(((X)>>8)&0xFF),HPUT8((X)&0xFF)) +#define @[HPUT32(X)@] (HPUT8(((X)>>24)&0xFF),HPUT8(((X)>>16)&0xFF),HPUT8(((X)>>8)&0xFF),HPUT8((X)&0xFF)) +@ + +The above macros test for buffer overruns\index{buffer overrun}; +allocating sufficient buffer space is done separately. + +Before writing a node, we will insert a test and increase the buffer if necessary. +@= +void hput_increase_buffer(uint32_t n); +#define @[HPUTX(N)@] @[(((hend-hpos) < (N))? hput_increase_buffer(N):(void)0)@] +#define HPUTNODE @[HPUTX(MAX_TAG_DISTANCE)@] +#define @[HPUTTAG(K,I)@] @|@[(HPUTNODE,@+DBGTAG(TAG(K,I),hpos),@+HPUT8(TAG(K,I)))@] +@ + +Fortunately the only data types that have an unbounded size are +strings\index{string} and texts\index{text}. +For these we insert specific tests. For all other cases a relatively +small upper bound on the maximum distance between two tags can be determined. +Currently the maximum distance between tags is 26 byte as can be determined +from the |hnode_size| array described in appendix~\secref{fastforward}. +The definition below uses a slightly larger value leaving some room +for future changes in the design of the short file format. + +@= +#define MAX_TAG_DISTANCE 32 +@ + +\subsection{Mapping a Short Format File to Memory} +In the following, we implement two alternatives to map a file into memory. +The first implementation, opens the file, gets its size, allocates memory, +and reads the file. The second implementation uses a call to |mmap|. + +Since modern computers with 64bit hardware have a huge address space, +using |mmap| to map the entire file into virtual memory is the most efficient way +to access a large file. ``Mapping'' is not the same as ``reading'' and it is +not the same as allocating precious memory, all that is done by the +operating system when needed. Mapping just reserves addresses. +There is one disadvantage of mapping: it typically locks the underlying file +and will not allow a separate process to modify it. This prevents using +this method for previewing a \HINT\ file while editing and recompiling it. +In this case, the first implementation, which has a copy of the file in memory, +is the better choice. To select the second implementation, define the macro |USE_MMAP|. + +The following functions map and unmap a short format input +file setting |hin_addr| to its address and |hin_size| to its size. +The value |hin_addr==NULL| indicates, that no file is open. +The variable |hin_time| is set to the time when the file was last modified. +It can be used to detect modifications of the file and reload it.\label{map} + +@= +char *hin_name=NULL; +uint64_t hin_size=0; +uint8_t *hin_addr=NULL; +uint64_t hin_time=0; +@ + +@= +#ifndef USE_MMAP +void hget_unmap(void) +{@+ if (hin_addr!=NULL) free(hin_addr); + hin_addr=NULL; + hin_size=0; +} +bool hget_map(void) +{ FILE *f; + struct stat st; + size_t s,t,u; + f= fopen(hin_name,"rb"); + if (f==NULL)@/ + { MESSAGE("Unable to open file: %s", hin_name);@+ return false;@+ } + if (stat(hin_name,&st)<0) + { MESSAGE("Unable to obtain file size: %s", hin_name); + fclose(f); + return false; + } + if (st.st_size==0) + { MESSAGE("File %s is empty", hin_name); + fclose(f); + return false; + } + if (hin_addr!=NULL) hget_unmap(); + hin_addr=malloc(st.st_size); + if (hin_addr==NULL) + { MESSAGE("Unable to allocate %ld byte for File %s", st.st_size,hin_name); + fclose(f); + return 0; + } + t=0;@+ + u=st.st_size; + do{ + s=fread(hin_addr+t,1,u,f); + if (s<=0) + { MESSAGE("Unable to read file %s",hin_name); + fclose(f); + free(hin_addr); + hin_addr=NULL; + return false; + } + t=t+s;@+ + u=u-s; + } while (u>0); + hin_size=st.st_size; + hin_time=st.st_mtime; + return true; +} + +#else + +#include + +void hget_unmap(void) +{@+ munmap(hin_addr,hin_size); + hin_addr=NULL; + hin_size=0; +} + +bool hget_map(void) +{ struct stat st; + int fd; + fd = open(hin_name, O_RDONLY, 0); + if (fd<0)@/ + { MESSAGE("Unable to open file %s", hin_name);@+ return false;@+ } + if (fstat(fd, &st)<0) + { MESSAGE("Unable to get file size"); + close(fd); + return false; + } + if (st.st_size==0) + { MESSAGE("File %s is empty",hin_name); + close(fd); + return false; + } + if (hin_addr!=NULL) hget_unmap(); + hin_size=st.st_size; + hin_time=st.st_mtime; + hin_addr= mmap(NULL,hin_size,PROT_READ,MAP_PRIVATE,fd, 0); + if (hin_addr==MAP_FAILED) + { close(fd); + hin_addr=NULL;hin_size=0; + MESSAGE("Unable to map file into memory"); + return 0; + } + close(fd); + return hin_size; +} +#endif + +@ +\subsection{Compression} +The short file format offers the possibility to store sections in +compressed\index{compression} form. We use the {\tt zlib}\index{zlib+{\tt zlib}} compression library\cite{zlib}\cite{RFC1950} +to deflate\index{deflate} and inflate\index{inflate} individual sections. When one of the following +functions is called, we can get the section buffer, the buffer size +and the size actually used from the directory entry. If a section +needs to be inflated, its size after decompression is found in the +|xsize| field; if a section needs to be deflated, its size after +compression will be known after deflating it. + +@s z_stream int + +@= + +static void hdecompress(uint16_t n) +{ z_stream z; /* decompression stream */ + uint8_t *buffer; + int i; + + DBG(DBGCOMPRESS,"Decompressing section %d from 0x%x to 0x%x byte\n",@|n, dir[n].size, dir[n].xsize); + z.zalloc = (alloc_func)0;@+ + z.zfree = (free_func)0;@+ + z.opaque = (voidpf)0; + z.next_in = hstart; + z.avail_in = hend-hstart; + if (inflateInit(&z)!=Z_OK) + QUIT("Unable to initialize decompression: %s",z.msg); + ALLOCATE(buffer,dir[n].xsize+MAX_TAG_DISTANCE,uint8_t); + DBG(DBGBUFFER,"Allocating output buffer size=0x%x, margin=0x%x\n",dir[n].xsize,MAX_TAG_DISTANCE); + z.next_out = buffer; + z.avail_out =dir[n].xsize+MAX_TAG_DISTANCE; + i= inflate(&z, Z_FINISH); + DBG(DBGCOMPRESS,"in: avail/total=0x%x/0x%lx "@|"out: avail/total=0x%x/0x%lx, return %d;\n",@| + z.avail_in,z.total_in, z.avail_out, z.total_out,i); + if (i!=Z_STREAM_END) + QUIT("Unable to complete decompression: %s",z.msg); + if (z.avail_in != 0) + QUIT("Decompression missed input data"); + if (z.total_out != dir[n].xsize) + QUIT("Decompression output size mismatch 0x%lx != 0x%x",z.total_out, dir[n].xsize ); + if (inflateEnd(&z)!=Z_OK) + QUIT("Unable to finalize decompression: %s",z.msg); + dir[n].buffer=buffer; + dir[n].bsize=dir[n].xsize; + hpos0=hpos=hstart=buffer; + hend=hstart+dir[n].xsize; +} +@ + + +@= +static void hcompress(uint16_t n) +{ z_stream z; /* compression stream */ + uint8_t *buffer; + int i; + if (dir[n].size==0) { dir[n].xsize=0;@+ return; @+} + DBG(DBGCOMPRESS,"Compressing section %d of size 0x%x\n",n, dir[n].size); + z.zalloc = (alloc_func)0;@+ + z.zfree = (free_func)0;@+ + z.opaque = (voidpf)0; + if (deflateInit(&z,Z_DEFAULT_COMPRESSION)!=Z_OK) + QUIT("Unable to initialize compression: %s",z.msg); + ALLOCATE(buffer,dir[n].size+MAX_TAG_DISTANCE,uint8_t); + z.next_out = buffer; + z.avail_out = dir[n].size+MAX_TAG_DISTANCE; + z.next_in = dir[n].buffer; + z.avail_in = dir[n].size; + i=deflate(&z, Z_FINISH); + DBG(DBGCOMPRESS,"deflate in: avail/total=0x%x/0x%lx out: avail/total=0x%x/0x%lx, return %d;\n",@| + z.avail_in,z.total_in, z.avail_out, z.total_out,i); + if (z.avail_in != 0) + QUIT("Compression missed input data"); + if (i!=Z_STREAM_END) + QUIT("Compression incomplete: %s",z.msg); + if (deflateEnd(&z)!=Z_OK) + QUIT("Unable to finalize compression: %s",z.msg); + DBG(DBGCOMPRESS,"Compressed 0x%lx byte to 0x%lx byte\n",@|z.total_in,z.total_out); + free(dir[n].buffer); + dir[n].buffer=buffer; + dir[n].bsize=dir[n].size+MAX_TAG_DISTANCE; + dir[n].xsize=dir[n].size; + dir[n].size=z.total_out; +} +@ + + + +\subsection{Reading Short Format Sections} +\gdef\subcodetitle{Sections}% + +After mapping the file at address |hin_addr| access to sections of the +file is provided by decompressing them if necessary and +setting the three pointers |hpos|, |hstart|, and +|hend|. + +To read sections of a short format input file, we use the function |hget_section|. + +\getcode +%\codesection{\getsymbol}\getindex{1}{3}{Files} + +@= +void hget_section(uint16_t n) +{ DBG(DBGDIR,"Reading section %d\n",n); + RNG("Section number",n,0,max_section_no); + if (dir[n].buffer!=NULL && dir[n].xsize>0) + { hpos0=hpos=hstart=dir[n].buffer; + hend=hstart+dir[n].xsize; + } + else + { hpos0=hpos=hstart=hin_addr+dir[n].pos; + hend=hstart+dir[n].size; + if (dir[n].xsize>0) hdecompress(n); + } +} +@ +\subsection{Writing Short Format Sections} +\gdef\subcodetitle{Sections}% + +To write a short format file, we allocate for each of the first three sections a +suitable buffer\index{buffer}, then fill these buffers, and finally write them +out in sequential order. + +@= +#define BUFFER_SIZE 0x400 +void new_output_buffers(void) +{ dir[0].bsize=dir[1].bsize=dir[2].bsize=BUFFER_SIZE; + DBG(DBGBUFFER,"Allocating output buffer size=0x%x, margin=0x%x\n",BUFFER_SIZE,MAX_TAG_DISTANCE); + ALLOCATE(dir[0].buffer,dir[0].bsize+MAX_TAG_DISTANCE,uint8_t); + ALLOCATE(dir[1].buffer,dir[1].bsize+MAX_TAG_DISTANCE,uint8_t); + ALLOCATE(dir[2].buffer,dir[2].bsize+MAX_TAG_DISTANCE,uint8_t); +} + +void hput_increase_buffer(uint32_t n) +{ size_t bsize; + uint32_t pos, pos0; + const double buffer_factor=1.4142136; /* $\sqrt 2$ */ + pos=hpos-hstart; pos0=hpos0-hstart; + bsize=dir[section_no].bsize*buffer_factor+0.5; + if (bsize=HINT_NO_POS) bsize=HINT_NO_POS; + if (bsize= +%token DIRECTORY "directory" +%token SECTION "entry" +@ + +@= +::@=directory@> :< return DIRECTORY; >: +::@=section@> :< return SECTION; >: +@ + +@= +directory_section: START DIRECTORY UNSIGNED @|{new_directory($3+1); new_output_buffers();} entry_list END ; +entry_list: @,@+ | entry_list entry; +entry: START SECTION UNSIGNED string END @/ + { RNG("Section number",$3,3,max_section_no); hset_entry(&(dir[$3]), $3,0,0,$4);}; +@ + + + +We use a dynamically allocated array +of directory entries to store the directory. + +@= +typedef struct { +uint64_t pos; +uint32_t size, xsize; +uint16_t section_no; +char *file_name; +uint8_t *buffer; +uint32_t bsize; +} entry_t; +@ + + +The function |new_directory| allocates the directory. + +@= +entry_t *dir=NULL; +uint16_t section_no, max_section_no; +void new_directory(uint32_t entries) +{ DBG(DBGDIR,"Creating directory with %d entries\n", entries); + RNG("Directory entries",entries,3,0x10000); + max_section_no=entries-1;@+ + ALLOCATE(dir,entries,entry_t); + dir[0].section_no=0; @+ dir[1].section_no=1; @+ dir[2].section_no=2; +} +@ + +The function |hset_entry| fills in the appropriate entry. +@= +void hset_entry(entry_t *e, uint16_t i, uint32_t size, uint32_t xsize, @|char *file_name) +{ e->section_no=i; + e->size=size; @+e->xsize=xsize; + if (file_name==NULL || *file_name==0) + e->file_name=NULL; + else + e->file_name=strdup(file_name); + DBG(DBGDIR,"Creating entry %d: \"%s\" size=0x%x xsize=0x%x\n",@|i,file_name,size,xsize); +} +@ + + +Writing the auxiliary files depends on the {\tt -f} and the {\tt -g} +option. + +@= + if ( !option_force && access(file_name,F_OK)==0) + { MESSAGE("File '%s' exists.\n"@| "To rewrite the file use the -f option.\n", + file_name); + continue; + } +@ + +The above code uses the |access| function, and we need to make sure it is defined: +@= +#ifdef WIN32 +#include +#define @[access(N,M)@] @[_access(N, M )@] +#define F_OK 0 +#else +#include +#endif +@ + +With the {\tt -g} option, filenames are considered global, and files +are written to the filesystem possibly overwriting the existing files. +For example a font embedded in a \HINT\ file might replace a font of +the same name in some operating systems font folder. +If the \HINT\ file is {\tt shrink}ed on one system and +{\tt stretch}ed on another system, this is usually not the desired behavior. +Without the {\tt -g} option,\label{absrel} the files will be written in two local directories. +The names of these directories are derived from the output file name, +replacing the extension ``{\tt .HINT}'' with ``{\tt .abs}'' if the original +filename contained an absolute path, and replacing it with ``{\tt .rel}'' +if the original filename contained a relative path. Inside these directories, +the path as given in the filename is retained. +When {\tt shrink}ing a \HINT\ file without the {\tt -g} option, +the original filenames can be reconstructed. + +@= +if (!option_global) +{ int path_length=(int)strlen(file_name); + @@; + if (file_name_length\t%s\n",file_name,stem_name); + file_name=stem_name; +} +@ + +@= + enum {absolute=0, relative=1} name_type; + char *aux_ext[2]={".abs/",".rel/"}; + int ext_length=5; + if (file_name[0]=='/') + { name_type=absolute; + file_name++; path_length--; + } + else if (path_length>3 && isalpha(file_name[0]) && + file_name[1]==':' && file_name[2]=='/') + { name_type=absolute; + file_name[1]='_'; + } + else + name_type=relative; +@ +It remains to create the directories along the path we might have constructed. +@= +{ char *path_end; + path_end=file_name+1; + while (*path_end!=0) + { if(*path_end=='/') + { struct stat s; + *path_end=0; + if (stat(file_name,&s)==-1) + { +#ifdef WIN32 + if (mkdir(file_name)!=0) +#else + @t\2\kern-1em@>if (mkdir(file_name,0777)!=0) +#endif + QUIT("Unable to create directory %s",file_name); + DBG(DBGDIR,"Creating directory %s\n",file_name); + } else if (!(S_IFDIR&s.st_mode)) + QUIT("Unable to create directory %s, file exists",file_name); + *path_end='/'; + } + path_end++; + } +} + + +@ + +\writecode +@= +@@; +extern char *stem_name; +extern int stem_length; + +void hget_section(uint16_t n); +void hwrite_aux_files(void) +{ int i; + DBG(DBGBASIC|DBGDIR,"Writing %d aux files\n",max_section_no-2); + for (i=3;i<=max_section_no;i++) + { FILE *f; + char * file_name=dir[i].file_name; + int file_name_length=0; + + + @@; + @@; + @@; + + f=fopen(file_name,"wb"); + if (f==NULL) + QUIT("Unable to open file '%s' for writing",file_name); + else + { size_t s; + hget_section(i); + DBG(DBGDIR,"Writing file %s\n",file_name); + s=fwrite(hstart,1,dir[i].size,f); + if (s!=dir[i].size) QUIT("writing file %s",file_name); + fclose(f); + } + } +} +@ + +We write the directory, and the directory entries +in long format using the following functions. +@= +static void hwrite_entry(int i) +{ hwrite_start(); + hwritef("section %u",dir[i].section_no);@+ hwrite_string(dir[i].file_name); + hwrite_end(); +} + +void hwrite_directory(void) +{ int i; + if (dir==NULL) QUIT("Directory not allocated"); + section_no=0; + hwritef("\n"); +} +@ + +\subsection{Directories in Short Format} +The directory\index{directory section} section of a short format file contains entries +for all sections including the directory section itself. After reading the +directory section, enough information---position and size---is available to +access any section directly. As usual, a directory entry starts and ends with +a tag byte. The kind part of an entry's tag is not used; it is always zero. +The value $s$ of the two least significant bits of the info part indicate +that sizes are stored using $s+1$ byte. The most significant bit of the info +part is 1 if the section is stored in compressed\index{compression} form. In this case the size +of the section is followed by the size of the section after decompressing it. +After the tag byte follows the section number. In the short format file, +section numbers must be strictly increasing and consecutive. This is redundant but helps +with checking. Then follows the size---or the sizes---of the section. After the size +follows the file name terminated by a zero byte. The file name might be an empty +string in which case there is just the zero byte. After the zero byte follows +a copy of the tag byte. + +Here is the macro and function to read a directory\index{directory entry} entry: +\gdef\subcodetitle{Directory Entries}% +\getcode + +@= +#define @[HGET_SIZE(I)@] \ + if ((I)&b100) { \ + if (((I)&b011)==0) s=HGET8,xs=HGET8; \ + else if (((I)&b011)==1) HGET16(s),HGET16(xs); \ + else if (((I)&b011)==2) HGET24(s),HGET24(xs); \ + else if (((I)&b011)==3) HGET32(s),HGET32(xs); \ + } \ + else { \ + if (((I)&b011)==0) s=HGET8; \ + else if (((I)&b011)==1) HGET16(s); \ + else if (((I)&b011)==2) HGET24(s); \ + else if (((I)&b011)==3) HGET32(s); \ + } + +#define @[HGET_ENTRY(I,E)@] \ +{ uint16_t i; \ + uint32_t s=0,xs=0; \ + char *file_name; \ + HGET16(i); @+HGET_SIZE(I); @+HGET_STRING(file_name); @/\ + hset_entry(&(E),i,s,xs,file_name); \ +} +@ + +@= +void hget_entry(entry_t *e) +{ @@; + DBG(DBGDIR,"Reading directory entry\n"); + switch(a) + { case TAG(0,b000+0): HGET_ENTRY(b000+0,*e);@+ break; + case TAG(0,b000+1): HGET_ENTRY(b000+1,*e);@+ break; + case TAG(0,b000+2): HGET_ENTRY(b000+2,*e);@+ break; + case TAG(0,b000+3): HGET_ENTRY(b000+3,*e);@+ break; + case TAG(0,b100+0): HGET_ENTRY(b100+0,*e);@+ break; + case TAG(0,b100+1): HGET_ENTRY(b100+1,*e);@+ break; + case TAG(0,b100+2): HGET_ENTRY(b100+2,*e);@+ break; + case TAG(0,b100+3): HGET_ENTRY(b100+3,*e);@+ break; + default: TAGERR(a); @+ break; + } + @@; +} +@ + +Because the first entry in the directory section describes the +directory section itself, we can not check its info bits in advance to determine +whether it is compressed or not. Therefore the directory section +starts with a root entry, which is always uncompressed. It describes +the remainder of the directory which follows. +There are two differences between the root entry and a normal entry: +it starts with the maximum section number instead of the section number zero, +and we set its position to the position of the +entry for section 1 (which might already be compressed). +The name of the directory section must be the empty string. +\gdef\subcodetitle{Directory Section}% +\getcode +@= +static void hget_root(entry_t *root) +{ DBG(DBGDIR,"Root entry at " SIZE_F "\n",hpos-hstart); + hget_entry(root); + root->pos=hpos-hstart; + max_section_no=root->section_no; + root->section_no=0; + if (max_section_no<2) QUIT("Sections 0, 1, and 2 are mandatory"); +} + +void hget_directory(void) +{ int i; + entry_t root={0}; + hget_root(&root); + DBG(DBGDIR,"Directory\n"); + new_directory(max_section_no+1); + dir[0]=root; + DBG(DBGDIR,"Directory entry 1 at 0x%"PRIx64"\n",dir[0].pos); + hget_section(0); + for (i=1;i<=max_section_no;i++)@/ + { hget_entry(&(dir[i]));@+ + dir[i].pos=dir[i-1].pos +dir[i-1].size;@+ + DBG(DBGDIR,"Section %d at 0x%"PRIx64"\n",i,dir[i].pos); + } +} + +void hclear_dir(void) +{ int i; + if (dir==NULL) return; + for (i=0;i<3;i++) /* currently the only compressed sections */ + if (dir[i].xsize>0 && dir[i].buffer!=NULL) free(dir[i].buffer); + free(dir); dir=NULL; +} + +@ + +When the \.{shrink} program writes the directory section in the short format, +it needs to know the sizes of all the sections---including the optional sections. +These sizes are not provided in the long format because it is safer and more +convenient to let the machine figure out the file sizes\index{file size}. + +@= +{ int i; + for (i=3;i<=max_section_no;i++) + { struct stat s; + char *file_name=dir[i].file_name; + int file_name_length=0; + @@; + if (stat(file_name,&s)!=0) + QUIT("Unable to obtain file size for '%s'",dir[i].file_name); + dir[i].size=s.st_size; + dir[i].xsize=0; + } +} +@ + +The computation of the sizes of the mandatory sections will be +explained later. +Armed with these preparations, we can put the directory into the \HINT\ file. + +\gdef\subcodetitle{Directory Section}% +\putcode +@= +static void hput_entry(entry_t *e) +{ uint8_t b; + if (e->size<0x100 && e->xsize<0x100) b=0; + else if (e->size<0x10000 &&e->xsize<0x10000) b=1; + else if (e->size<0x1000000 &&e->xsize<0x1000000) b=2; + else b=3; + if (e->xsize!=0) b =b|b100; + DBG(DBGTAGS,"Directory entry no=%d size=0x%x xsize=0x%x\n",e->section_no, e->size, e->xsize); + HPUTTAG(0,b);@/ + HPUT16(e->section_no); + switch (b) { + case 0: HPUT8(e->size);@+break; + case 1: HPUT16(e->size);@+break; + case 2: HPUT24(e->size);@+break; + case 3: HPUT32(e->size);@+break; + case b100|0: HPUT8(e->size);@+HPUT8(e->xsize);@+break; + case b100|1: HPUT16(e->size);@+HPUT16(e->xsize);@+break; + case b100|2: HPUT24(e->size);@+HPUT24(e->xsize);@+break; + case b100|3: HPUT32(e->size);@+HPUT32(e->xsize);@+break; + default: QUIT("Can't happen");@+ break; + } + hput_string(e->file_name);@/ + DBGTAG(TAG(0,b),hpos);@+HPUT8(TAG(0,b)); +} + +static void hput_directory_start(void) +{ DBG(DBGDIR,"Directory Section\n"); + section_no=0; + hpos=hstart=dir[0].buffer; + hend=hstart+dir[0].bsize; +} +static void hput_directory_end(void) +{ dir[0].size=hpos-hstart; + DBG(DBGDIR,"End Directory Section size=0x%x\n",dir[0].size); +} + +static size_t hput_root(void) +{ uint8_t buffer[MAX_TAG_DISTANCE]; + size_t s; + hpos=hstart=buffer; + hend=hstart+MAX_TAG_DISTANCE; + dir[0].section_no=max_section_no; + hput_entry(&dir[0]); + s=hput_data(0, hstart,hpos-hstart); + DBG(DBGDIR,@["Writing root size=" SIZE_F "\n"@],s); + return s; +} + +extern int option_compress; +void hput_directory(void) +{ int i; + @@; + if (option_compress) { hcompress(1); @+hcompress(2); @+} + hput_directory_start(); + for (i=1; i<=max_section_no; i++) + { dir[i].pos=dir[i-1].pos+dir[i-1].size; + DBG(DBGDIR,"writing entry %u at 0x%" PRIx64 "\n",i, dir[i].pos); + hput_entry(&dir[i]); + } + hput_directory_end(); + if (option_compress) hcompress(0); +} + +@ + + +To conclude this section, here is the function that adds the files that +are described in the directory entries 3 and above to a \HINT\ file in short format. +Where these files are found depends on the {\tt -g} option. +With that option given, the file names of the directory entries are used unchanged. +Without that option, the files are found in the {|hin_name|\tt .abs} and {|hin_name|\tt .rel} +directories, as described in section~\secref{absrel}. + +\gdef\subcodetitle{Optional Sections}% +\putcode +@= +static void hput_optional_sections(void) +{ int i; + DBG(DBGDIR,"Optional Sections\n"); + for (i=3; i<=max_section_no; i++)@/ + { FILE *f; + size_t fsize; + char *file_name=dir[i].file_name; + int file_name_length=0; + DBG(DBGDIR,"file %d: %s\n",dir[i].section_no,file_name); + if (dir[i].xsize!=0) @/ + DBG(DBGDIR,"Compressing of auxiliary files currently not supported"); + @@; + f=fopen(file_name,"rb"); + if (f==NULL) QUIT("Unable to read section %d, file %s", + dir[i].section_no,file_name); + fsize=0; + while (!feof(f))@/ + { size_t s,t; + char buffer[1<<13]; /* 8kByte */ + s=fread(buffer,1,1<<13,f);@/ + t=fwrite(buffer,1,s,hout); + if (s!=t) QUIT("writing file %s",file_name); + fsize=fsize+t; + } + fclose(f); + if (fsize!=dir[i].size) + QUIT(@["File size " SIZE_F " does not match directory size %u"@],@|fsize,dir[i].size); + } +} +@ + + + +\section{Definition Section}\index{definition section} +\label{defsection} +In a typical \HINT\ file, there are many things that are used over and over again. +For example the interword glue of a specific font or the indentation of +the first line of a paragraph. The definition section contains this information so that +it can be referenced in the content section by a simple reference number. +In addition there are a few parameters that guide the routines of \TeX. +An example is the ``above display skip'', which controls the amount of white space +inserted above a displayed equation, or the ``hyphen penalty'' that tells \TeX\ +the ``\ae sthetic cost'' of ending a line with a hyphenated word. These parameters +also get their values in the definition section as explained in section~\secref{defaults}. + + +The most simple way to store these definitions is to store them in an array indexed by the +reference numbers. +To simplify the dynamic allocation of these arrays, the list of definitions +will always start with the list of maximum\index{maximum values} values: a list that contains +for each node type the maximum reference number used. + +In the long format, the definition section starts with the keyword \.{definitions}, +followed by the list of maximum values, +followed by the definitions proper. + +When writing the short format, we start by positioning the output stream at the beginning of +the definition buffer and we end with recording the size of the definition section +in the directory. + +\readcode +@s definition_section symbol +@s definition_list symbol +@s definition symbol +@s DEFINITIONS symbol +@= +%token DEFINITIONS "definitions" +@ + +@= +::@=definitions@> :< return DEFINITIONS; >: +@ + +@= +definition_section: START DEFINITIONS { hput_definitions_start();}@/ + max_definitions definition_list @/ + END {hput_definitions_end();}; +definition_list: @+ | definition_list def_node; +@ + +\writecode +@= +void hwrite_definitions_start(void) +{ section_no=1; @+hwritef("\n"); +} +@ + + + +@= +void hget_definition_section(void) +{ DBG(DBGBASIC|DBGDEF,"Definitions\n"); + hget_section(1); + hwrite_definitions_start(); + DBG(DBGDEF,"List of maximum values\n"); + hget_max_definitions(); + @@; + hwrite_max_definitions(); + DBG(DBGDEF,"List of definitions\n"); + while (hpos= +void hput_definitions_start(void) +{ DBG(DBGDEF,"Definition Section\n"); + section_no=1; + hpos=hstart=dir[1].buffer; + hend=hstart+dir[1].bsize; +} +void hput_definitions_end(void) +{ dir[1].size=hpos-hstart; + DBG(DBGDEF,"End Definition Section size=0x%x\n",dir[1].size); +} +@ +\gdef\codetitle{Definitions} +\hascode +\subsection{Maximum Values}\index{maximum values} +To help implementations allocating the right amount of memory for the +definitions, the definition section starts with a list of maximum +values. For each kind of node, we store the maximum valid reference +number in the array |max_ref| which is indexed by the kind-values. +For a reference number |n| and kind-value $k$ we have +$0\le n\le |max_ref[k]|$. +To make sure that a hint file without any definitions +will work, some definitions have default values. +The initialization of default and maximum values is described +in section~\secref{defaults}. The maximum +reference number that has a default value is stored in the array +|max_default|. +We have $-1 \le |max_default[k]| \le |max_ref[k]| < 2^{16}$, +and for most $k$ even $|max_ref[k]| < 2^{8}$. +Specifying maximum values that are lower than the +default\index{default value} values is not allowed in the short +format; in the long format, lower values are silently ignored. Some +default values are permanently fixed; for example the zero glue with +reference number |zero_skip_no| must never change. The array +|max_fixed| stores the maximum reference number that has a fixed value for a +given kind. Definitions with reference numbers less or equal than the +corresponding |max_fixed[k]| number are disallowed. Usually we have +$-1 \le |max_fixed[k]| \le |max_default[k]| $, but if for a kind-value +$k$ no definitions, and hence no maximum values are allowed, we set +$|max_fixed[k]|=|0x10000|>|max_default[k]| $. + + +We use the |max_ref| array whenever we find a +reference number in the input to check if it is within the proper range. + +@= +#define @[REF_RNG(K,N)@] if ((int)(N)>max_ref[K]) QUIT("Reference %d to %s out of range [0 - %d]",\ + (N),definition_name[K],max_ref[K]) +@ + +In the long format file, the list of maximum values starts with + ``\.{}'', and it ends with ``\.{>}''. In the short format, +we start the list of maximums with a |list_kind| tag and end it with +a |list_kind| tag. Each maximum value is preceded and followed by a +tag byte with the appropriate kind-value. The info value has its |b001| bit +cleared if the maximum value is in the range 0 to |0xFF| and fits into a +single byte; the info value hast its |b001| bit set if it fits into two byte. +Currently only the |label_kind| may need to use two byte. +@= +#define MAX_REF(K) ((K)==label_kind?0xFFFF:0xFF) +@ + +Other info values are reserved for future extensions. +After reading the maximum values, we initialize the data structures for +the definitions. + + +\readcode +@s max_list symbol +@s max_value symbol +@s max_definitions symbol +@s MAX symbol +@= +%token MAX "max" +@ + +@= +::@=max@> :< return MAX; >: +@ +@= +max_definitions: START MAX max_list END @| + { @@;@+ hput_max_definitions(); }; + +max_list:@+ | max_list START max_value END; + +max_value: FONT UNSIGNED { hset_max(font_kind,$2); } + | INTEGER UNSIGNED { hset_max(int_kind,$2); } + | DIMEN UNSIGNED { hset_max(dimen_kind,$2); } + | LIGATURE UNSIGNED { hset_max(ligature_kind,$2); } + | DISC UNSIGNED { hset_max(disc_kind,$2); } + | GLUE UNSIGNED { hset_max(glue_kind,$2); } + | LANGUAGE UNSIGNED { hset_max(language_kind,$2); } + | RULE UNSIGNED { hset_max(rule_kind,$2); } + | IMAGE UNSIGNED { hset_max(image_kind,$2); } + | LEADERS UNSIGNED { hset_max(leaders_kind,$2); } + | BASELINE UNSIGNED { hset_max(baseline_kind,$2); } + | XDIMEN UNSIGNED { hset_max(xdimen_kind,$2); } + | PARAM UNSIGNED { hset_max(param_kind,$2); } + | STREAMDEF UNSIGNED { hset_max(stream_kind,$2); } + | PAGE UNSIGNED { hset_max(page_kind,$2); } + | RANGE UNSIGNED { hset_max(range_kind,$2); } + | LABEL UNSIGNED { hset_max(label_kind,$2); }; + +@ + +@= +void hset_max(kind_t k, int n) +{ DBG(DBGDEF,"Setting max %s to %d\n",definition_name[k],n); + RNG("Maximum",n,max_fixed[k]+1,MAX_REF(k)); + if (n>max_ref[k]) + max_ref[k]=n; +} +@ + +\writecode +@= +void hwrite_max_definitions(void) +{ kind_t k; + hwrite_start();@+ + hwritef("max"); + for (k=0; k<32;k++) + if (max_ref[k]>max_default[k])@/ + {@+ switch (k) + { @@; + default: + hwrite_start(); + hwritef("%s %d",definition_name[k], max_ref[k]); + hwrite_end(); + break; + } + } + hwrite_end(); +} +@ + +\getcode +@= +void hget_max_definitions(void) +{ kind_t k; + @@; + if (a!=TAG(list_kind,0)) QUIT("Start of maximum list expected"); + for(k= 0;k<32;k++)max_ref[k]= max_default[k]; max_outline=-1; + while (true) @/ + { int n; + if (hpos>=hend) QUIT("Unexpected end of maximum list"); + node_pos=hpos-hstart; + HGETTAG(a);@+ + k=KIND(a);@+ + if (k==list_kind) break; + if (INFO(a)&b001) HGET16(n); @+else n=HGET8; + switch (a) + { @@; + default: + if (max_fixed[k]>max_default[k]) + QUIT("Maximum value for kind %s not supported",definition_name[k]); + RNG("Maximum number",n,max_default[k],MAX_REF(k)); + max_ref[k]=n; + DBG(DBGDEF,"max(%s) = %d\n",definition_name[k],max_ref[k]); + break; + } + @@; + } + if (INFO(a)!=0) QUIT("End of maximum list with info %d", INFO(a)); +} +@ + +\putcode + +@= +void hput_max_definitions(void) +{ kind_t k; + DBG(DBGDEF,"Writing Max Definitions\n"); + HPUTTAG(list_kind,0); + for (k=0; k<32; k++) + if (max_ref[k]>max_default[k]) + { uint32_t pos=hpos++-hstart; + DBG(DBGDEF,"max(%s) = %d\n",definition_name[k],max_ref[k]); + hput_tags(pos,TAG(k,hput_n(max_ref[k])-1)); + } + @@; + HPUTTAG(list_kind,0); + DBG(DBGDEF,"Writing Max Definitions End\n"); +} +@ + + +\subsection{Definitions}\label{definitions} +A definition\index{definition section} associates a reference number +with a content node. Here is an example: A glue definition associates +a glue number, for example 71, with a glue specification. In the long +format this might look like ``{\tt \.{<}glue *71 4pt plus 5pt minus +0.5pt\.{>}}'' which makes glue number 71 refer to a 4pt glue with a +stretchability of 5pt and a shrinkability of 0.5pt. +Such a glue definition differs from a normal glue node just by an extra +byte value immediately following the keyword respectively start byte. + +Whenever we need this glue in the content section, we can say +``{\tt \.{<}glue *71\.{>}}''. Because we restrict the number of glue definitions +to at most 256, a single byte is sufficient to +store the reference number. The \.{shrink} and \.{stretch} programs +will, however, not bother to store glue definitions. Instead they will +write them in the new format immediately to the output. + +The parser will handle definitions in any order, but the order is relevant +if a definition references another definition, and of course, +it never does any harm to present definitions in a systematic way. + +As a rule, the definition of a reference must always precede the +use of that reference. While this is always the case for +references in the content section, it restricts the use of +references inside the definition section. + +The definitions for integers, dimensions, extended dimensions, + languages, rules, ligatures, and images are ``simple''. +They never contain references and so it is always possible to list them first. +The definition of glues may contain extended dimensions, +the definitions of baselines may reference glue nodes, and +the definitions of parameter lists contain definitions of integers, dimensions, +and glues. So these definitions should follow in this order. + +The definitions of leaders and discretionary breaks allow boxes. +While these boxes are usually +quite simple, they may contain arbitrary references---including again +references to leaders and discretionary breaks. So, at least in principle, +they might impose complex (or even unsatisfiable) restrictions +on the order of those definitions. + +The definitions of fonts contain not only ``simple'' definitions +but also the definitions of interword glues and hyphens +introducing additional ordering restrictions. +The definition of hyphens regularly contain glyphs which in turn reference +a font---typically the font that just gets defined. +Therefore we relax the define before use policy for glyphs: +Glyphs may reference a font before the font is defined. + +The definitions of page templates contain lists of arbitrary content +nodes, and while the boxes inside leaders or discretionary breaks tend to be simple, +the content of page templates is often quite complex. +Page templates are probably the source of most ordering restrictions. +Placing page templates towards the end of the list of definitions +might be a good idea. +% +A special case are stream definitions. These occur only as part of +the corresponding page template definition and are listed at its end. +So references to them will occur in the page template always before their +definition. +% +Finally, the definitions of page ranges always reference a page template +and they should come after the page template definitions. +For technical reasons explained in section~\secref{labels}, +definitions of labels and outlines come last. + +To avoid complex dependencies, an application can always choose not to +use references in the definition section. There are only three types of +nodes where references can not be avoided: fonts are referenced in glyph nodes, +labels are referenced in outlines, +and languages are referenced in boxes or page templates. +Possible ordering restrictions can be satisfied if languages are defined early. +To check the define before use policy, we use an array of bitvectors, +but we limit checking to the first 256 references. +We have for every reference number $|N|<256$ and every kind |K| a single +bit which is set if and only if the corresponding reference is defined. + +@= +uint32_t definition_bits[0x100/32][32]={{0}}; + +#define @[SET_DBIT(N,K)@] ((N)>0xFF?1:(definition_bits[N/32][K]|=(1<<((N)&(32-1))))) +#define @[GET_DBIT(N,K)@] ((N)>0xFF?1:((definition_bits[N/32][K]>>((N)&(32-1)))&1)) +#define @[DEF(D,K,N)@] (D).k=K;@+ (D).n=(N);@+SET_DBIT((D).n,(D).k);\ + DBG(DBGDEF,"Defining %s %d\n",definition_name[(D).k],(D).n);\ + RNG("Definition",(D).n,max_fixed[(D).k]+1,max_ref[(D).k]); +#define @[REF(K,N)@] REF_RNG(K,N);if(!GET_DBIT(N,K)) \ + QUIT("Reference %d to %s before definition",(N),definition_name[K]) +@ + +@= +definition_bits[0][int_kind]=(1<<(MAX_INT_DEFAULT+1))-1; +definition_bits[0][dimen_kind]=(1<<(MAX_DIMEN_DEFAULT+1))-1; +definition_bits[0][xdimen_kind]=(1<<(MAX_XDIMEN_DEFAULT+1))-1; +definition_bits[0][glue_kind]=(1<<(MAX_GLUE_DEFAULT+1))-1; +definition_bits[0][baseline_kind]=(1<<(MAX_BASELINE_DEFAULT+1))-1; +definition_bits[0][page_kind]=(1<<(MAX_PAGE_DEFAULT+1))-1; +definition_bits[0][stream_kind]=(1<<(MAX_STREAM_DEFAULT+1))-1; +definition_bits[0][range_kind]=(1<<(MAX_RANGE_DEFAULT+1))-1; +@ + +\goodbreak +\vbox{\readcode\vskip -\baselineskip\putcode} + + +@s font symbol +@= + +%type def_node +@ + +@= +def_node: + start FONT ref font END @| { DEF($$,font_kind,$3);@+ hput_tags($1,$4);@+} +| start INTEGER ref integer END @| { DEF($$,int_kind,$3);@+ hput_tags($1,hput_int($4));@+} +| start DIMEN ref dimension END @| { DEF($$,dimen_kind,$3);@+ hput_tags($1,hput_dimen($4));} +| start LANGUAGE ref string END @| { DEF($$,language_kind,$3);@+hput_string($4); hput_tags($1,TAG(language_kind,0));} +| start GLUE ref glue END @| { DEF($$,glue_kind,$3);@+ hput_tags($1,hput_glue(&($4)));} +| start XDIMEN ref xdimen END @| { DEF($$,xdimen_kind,$3);@+ hput_tags($1,hput_xdimen(&($4)));} +| start RULE ref rule END @| { DEF($$,rule_kind,$3);@+ hput_tags($1,hput_rule(&($4)));} +| start LEADERS ref leaders END @| { DEF($$,leaders_kind,$3);@+ hput_tags($1,TAG(leaders_kind, $4));} +| start BASELINE ref baseline END @| { DEF($$,baseline_kind,$3);@+hput_tags($1,TAG(baseline_kind, $4));@+} +| start LIGATURE ref ligature END @| { DEF($$,ligature_kind,$3);@+hput_tags($1,hput_ligature(&($4)));} +| start DISC ref disc END @| { DEF($$,disc_kind,$3);@+ hput_tags($1,hput_disc(&($4)));} +| start IMAGE ref image END @| { DEF($$,image_kind,$3);@+ hput_tags($1,hput_image(&($4)));} +| start PARAM ref parameters END @| { DEF($$,param_kind,$3);@+ hput_tags($1,hput_list($1+2,&($4)));} +| start PAGE ref page END @| { DEF($$,page_kind,$3);@+ hput_tags($1,TAG(page_kind,0));}; +@ + +There are a few cases where one wants to define a reference by a reference. +For example, a \HINT\ file may want to set the {\tt parfillskip} glue to zero. +While there are multiple ways to define the zero glue, the canonical way is a reference +using the |zero_glue_no|. All these cases have in common that the reference to be defined +is one of the default references and the defining reference is one of the fixed references. +We add a few parsing rules and a testing macro for those cases where the number +of default definitions is greater than the number of fixed definitions. + +@= +#define @[DEF_REF(D,K,M,N)@] DEF(D,K,M);\ +if ((M)>max_default[K]) QUIT("Defining non default reference %d for %s",M,definition_name[K]); \ +if ((N)>max_fixed[K]) QUIT("Defining reference %d for %s by non fixed reference %d",M,definition_name[K],N); +@ + +@= +def_node: + start INTEGER ref ref END @/{DEF_REF($$,int_kind,$3,$4); hput_tags($1,TAG(int_kind,0)); } +| start DIMEN ref ref END @/{DEF_REF($$,dimen_kind,$3,$4); hput_tags($1,TAG(dimen_kind,0)); } +| start GLUE ref ref END @/{DEF_REF($$,glue_kind,$3,$4); hput_tags($1,TAG(glue_kind,0)); }; +@ + + + + +\goodbreak +\vbox{\getcode\vskip -\baselineskip\writecode} + +@= +void hget_definition(int n, uint8_t a, uint32_t node_pos) +{@+ switch(KIND(a)) + { case font_kind: hget_font_def(n);@+ break; + case param_kind: + {@+ list_t l; @+HGET_LIST(INFO(a),l); @+hwrite_parameters(&l); @+ break;@+} + case page_kind: hget_page(); @+break; + case dimen_kind: hget_dimen(a); @+break; + case xdimen_kind: + {@+ xdimen_t x; @+hget_xdimen(a,&x); @+hwrite_xdimen(&x); @+break;@+ } + case language_kind: + if (INFO(a)!=b000) + QUIT("Info value of language definition must be zero"); + else + { char *n; HGET_STRING(n);@+ hwrite_string(n); } + break; + default: + hget_content(a); @+break; + } +} + + +void hget_def_node() +{ kind_t k; + + @@; + k=KIND(a); + if (k==label_kind) + hget_outline_or_label_def(INFO(a),node_pos); + else + { int n; + n=HGET8; + if (k!=range_kind) REF_RNG(k,n); + SET_DBIT(n,k); + if (k==range_kind) + hget_range(INFO(a),n); + else + { hwrite_start(); @+hwritef("%s *%d",definition_name[k],n); + hget_definition(n,a,node_pos); + hwrite_end(); + } + if(n>max_ref[k] || n <= max_fixed[k]) + QUIT("Definition %d for %s out of range [%d - %d]",@| + n, definition_name[k],max_fixed[k]+1,max_ref[k]); + } + if (max_fixed[k]>max_default[k]) + QUIT("Definitions for kind %s not supported", definition_name[k]); + @@; +} +@ + + + +\subsection{Parameter Lists}\label{paramlist}\index{parameter list} +Because the content section is a ``stateless'' list of nodes, the +definitions we see in the definition section can never change. It is +however necessary to make occasionally local modifications of some of +these definitions, because some definitions are parameters of the +algorithms borrowed from \TeX. Nodes that need such modifications, for +example the paragraph nodes that are passed to \TeX's line breaking +algorithm, contain a list of local definitions called parameters. +Typically sets of related parameters are needed. To facilitate a +simple reference to such a set of parameters, we allow predefined +parameter lists that can be referenced by a single number. The +parameters of \TeX's routines are quite basic---integers\index{integer}, +dimensions\index{dimension}, and glues\index{glue}---and all +of them have default values. +Therefore we restrict the definitions in parameter lists to such +basic definitions. + +@= +void check_param_def(ref_t *df) +{ if(df->k!=int_kind && df->k!=dimen_kind && @| df->k!=glue_kind) + QUIT("Kind %s not allowed in parameter list", definition_name[df->k]); + if(df->n<=max_fixed[df->k] || max_default[df->k]n) + QUIT("Parameter %d for %s not allowed in parameter list", df->n, definition_name[df->k]); +} +@ + +The definitions below repeat the definitions we have seen for lists in section~\secref{plainlists} +with small modifications. For example we use the kind-value |param_kind|. An empty parameter list +is omitted in the long format as well as in the short format. + +\goodbreak +\vbox{\readcode\vskip -\baselineskip\putcode} + +@s PARAM symbol +@s def_list symbol +@s parameters_node symbol +@s def_node symbol +@s parameters symbol +@s empty_param_list symbol +@s non_empty_param_list symbol + +@= +%token PARAM "param" +%type def_list +%type parameters +@ + +@= +::@=param@> :< return PARAM; >: +@ +@= +def_list: position @+ + | def_list def_node {check_param_def(&($2));}; +parameters: estimate def_list { $$.p=$2; $$.k=param_kind; $$.s=(hpos-hstart)-$2;}; +@ + +Using a parsing rule like +``\nts{param\_list}: \nts{start} \ts{PARAM} \nts{parameters} \ts{END}'', +an empty parameter list will be written as ``\.{}''. +This looks ugly and seems like unnecessary syntax. It would be nicer +if an empty parameter list could simply be omitted. +Generating an empty parameter list for an omitted parameter list +is however a bit tricky. +Consider the sequence ``\.{} \.{}'' versus +the sequence ``\.{}''. In the latter case, +the parser will notice the missing parameter list +when it encounters the \.{hbox} token. +Of course it is not a good idea to augment the rules for the \.{hbox} with +a special test for the missing empty parameter list. +It is better to insert an empty parameter list before parsing the first ``\.{<}'' token +and remove it again if a non-empty parameter list has been detected. +This can be accomplished by the following two rules. + +@= +empty_param_list: position { HPUTX(2); hpos++; hput_tags($1,TAG(param_kind,1));}; +non_empty_param_list: start PARAM {hpos=hpos-2;} parameters END @/ + { @+ hput_tags($1-2,hput_list($1-1,&($4)));@+}; +@ + +\writecode +@= +void hwrite_parameters(list_t *l) +{ uint32_t h=hpos-hstart, e=hend-hstart; /* save |hpos| and |hend| */ + hpos=l->p+hstart;@+ hend=hpos+l->s; + if (l->s>0xFF) hwritef(" %d",l->s); + while(hposs!=0) @/ + { hwrite_start();@+ + hwritef("param"); + hwrite_parameters(l); + hwrite_end(); + } +} +@ + +\getcode +@= +void hget_param_list(list_t *l) +{ @+if (KIND(*hpos)!=param_kind) @/ + QUIT("Parameter list expected at 0x%x", (uint32_t)(hpos-hstart)); + else hget_list(l); +} +@ + + + +\subsection{Fonts}\label{fonts} +Another definition that has no corresponding content node is the +font\index{font} definition. Fonts by themselves do not constitute +content, instead they are used in glyph\index{glyph} nodes. +Further, fonts are never directly embedded in a content node; in a content node, a +font is always specified by its font number. This limits the number of +fonts that can be used in a \HINT\ file to at most 256. + +A long format font definition starts with the keyword ``\.{font}'' and +is followed by the font number, as usual prefixed by an asterisk. Then +comes the font specification with the font size, the font +name, the section number of the \TeX\ font metric file, and the +section number of the file containing the glyphs for the font. +The \HINT\ format supports \.{.pk} files, the traditional font format +for \TeX, and the more modern PostScript Type 1 fonts, +TrueType fonts, and OpenType fonts. + +The format of font definitions will probably change in future +versions of the \HINT\ file format. +For example, \.{.pk} files might be replaced entirely by PostScript Type 1 fonts. +Also \HINT\ needs the \TeX\ font metric files only to obtain the sizes +of characters when running \TeX's line breaking algorithm. +But for many TrueType fonts there are no \TeX\ font metric files, +while the necessary information about character sizes should be easy +to obtain. +Another information, that is currently missing from font definitions, +is the fonts character encoding. + +In a \HINT\ file, text is represented as a sequence of numbers called +character codes. \HINT\ files use the UTF-8 character encoding +scheme (CES) to map these numbers to their representation as byte +sequences. For example the number ``|0xE4|'' is encoded as the byte +sequence ``|0xC3| |0xA4|''. The same number |0xE4| now can represent +different characters depending on the coded character set (CCS). For +example in the common ISO-8859-1 (Latin 1) encoding the number |0xE4| +is the umlaut ``\"a'' where as in the ISO-8859-7 (Latin/Greek) it is +the Greek letter ``$\delta$'' and in the EBCDIC encoding, used on IBM +mainframes, it is the upper case letter ``U''. + +The character encoding is +irrelevant for rendering a \HINT\ file as long as the character codes +in the glyph nodes are consistent with the character codes used in the font +file, but the character encoding is necessary for all programs that +need to ``understand'' the content of the \HINT\ file. For example +programs that want to translate a \HINT\ document to a different language, +or for text-to-speech conversion. + +The Internet Engineering Task Force IETF has established a character set +registry\cite{ietf:charset-mib} that defines an enumeration of all +registered coded character sets\cite{iana:charset-mib}. The coded +character set numbers are in the range 1--2999. +This encoding number, as given in~\cite{iana:charset}, +might be one possibility for specifying the font encoding as +part of a font definition. + +Currently, it is only required that a font specifies +an interword glue and a default discretionary break. After that comes +a list of up to 12 font specific parameters. + +The font size specifies the desired ``at size''\index{font at size} +which might be different from the ``design size''\index{font design size} +of the font as stored in the \.{.tfm} file. + +In the short format, the font specification is given in the same order +as in the long format. + +Our internal representation of a font just stores the font name +because in the long format we add the font name as a comment to glyph +nodes. + + +@= +char **hfont_name; /* dynamically allocated array of font names */ +@ + +@= +#define MAX_FONT_PARAMS 11 +@ + +@= +ALLOCATE(hfont_name,max_ref[font_kind]+1,char *); +@ + + + +\readcode +@s FONT symbol +@s fref symbol +@s font_param_list symbol +@s font_param symbol +@s font_head symbol + +@= +%token FONT "font" +%type font font_head +@ + +@= +::@=font@> :< return FONT; >: +@ + +Note that we set the definition bit early because the definition of font |f| +might involve glyphs that reference font |f| (or other fonts). + +@=@/ + +font: font_head font_param_list; + +font_head: string dimension UNSIGNED UNSIGNED @/ + {uint8_t f=$@&0; SET_DBIT(f,font_kind); @+hfont_name[f]=strdup($1); $$=hput_font_head(f,hfont_name[f],$2,$3,$4);}; + +font_param_list: glue_node disc_node @+ | font_param_list font_param ; + +font_param: @/ + start PENALTY fref penalty END { hput_tags($1,hput_int($4));} +| start KERN fref kern END { hput_tags($1,hput_kern(&($4)));} +| start LIGATURE fref ligature END { hput_tags($1,hput_ligature(&($4)));} +| start DISC fref disc END { hput_tags($1,hput_disc(&($4)));} +| start GLUE fref glue END { hput_tags($1,hput_glue(&($4)));} +| start LANGUAGE fref string END { hput_string($4);hput_tags($1,TAG(language_kind,0));} +| start RULE fref rule END { hput_tags($1,hput_rule(&($4)));} +| start IMAGE fref image END { hput_tags($1,hput_image(&($4)));}; + +fref: ref @/{ RNG("Font parameter",$1,0,MAX_FONT_PARAMS); }; +@ + +\goodbreak +\vbox{\getcode\vskip -\baselineskip\writecode} + +@= +static void hget_font_params(void) +{ disc_t h; + hget_glue_node(); + hget_disc_node(&(h));@+ hwrite_disc_node(&h); + DBG(DBGDEF,"Start font parameters\n"); + while (KIND(*hpos)!=font_kind)@/ + { ref_t df; + @@; + df.k=KIND(a); + df.n=HGET8; + DBG(DBGDEF,"Reading font parameter %d: %s\n",df.n, definition_name[df.k]); + if (df.k!=penalty_kind && df.k!=kern_kind && df.k!=ligature_kind && @| + df.k!=disc_kind && df.k!=glue_kind && df.k!=language_kind && @| df.k!=rule_kind && df.k!=image_kind) + QUIT("Font parameter %d has invalid type %s",df.n, content_name[df.n]); + RNG("Font parameter",df.n,0,MAX_FONT_PARAMS); + hwrite_start(); @+ hwritef("%s *%d",content_name[KIND(a)],df.n); + hget_definition(df.n,a,node_pos); + hwrite_end(); + @@; + } + DBG(DBGDEF,"End font parameters\n"); +} + + +void hget_font_def(uint8_t f) +{ char *n; @+dimen_t s=0;@+uint16_t m,y; + HGET_STRING(n);@+ hwrite_string(n);@+ hfont_name[f]=strdup(n); + HGET32(s); @+ hwrite_dimension(s); + DBG(DBGDEF,"Font %s size 0x%x\n", n, s); + HGET16(m); @+RNG("Font metrics",m,3,max_section_no); + HGET16(y); @+RNG("Font glyphs",y,3,max_section_no); + hwritef(" %d %d",m,y); + hget_font_params(); + DBG(DBGDEF,"End font definition\n"); +} +@ + +\putcode +@= +uint8_t hput_font_head(uint8_t f, char *n, dimen_t s, @| uint16_t m, uint16_t y) +{ info_t i=b000; + DBG(DBGDEF,"Defining font %d (%s) size 0x%x\n", f, n, s); + hput_string(n); + HPUT32(s);@+ + HPUT16(m); @+HPUT16(y); + return TAG(font_kind,i); +} +@ + + + +\subsection{References}\label{reference} +We have seen how to make definitions, now let's see how to +reference\index{reference} them. In the long form, we can simply +write the reference number, after the keyword like this: +``{\tt \.{<}glue *17\.{>}}''. +The asterisk\index{asterisk} is necessary to keep apart, +for example, a penalty with value 50, +written ``{\tt \.{<}penalty 50\.{>}}'', +from a penalty referencing the integer +definition number 50, written ``{\tt \.{<}penalty *50\.{>}}''. + +\goodbreak +\vbox{\readcode\vskip -\baselineskip\putcode} + +@= +xdimen_ref: ref { REF(xdimen_kind,$1);}; +param_ref: ref { REF(param_kind,$1); }; +stream_ref: ref { REF_RNG(stream_kind,$1); }; + + +content_node: + start PENALTY ref END @/{ REF(penalty_kind,$3); @+ hput_tags($1,TAG(penalty_kind,0)); } +|start KERN explicit ref END @/ + { REF(dimen_kind,$4); @+ hput_tags($1,TAG(kern_kind,($3)?b100:b000)); } +|start KERN explicit XDIMEN ref END @/ + { REF(xdimen_kind,$5); @+hput_tags($1,TAG(kern_kind,($3)?b101:b001)); } +|start GLUE ref END @/{ REF(glue_kind,$3); @+ hput_tags($1,TAG(glue_kind,0)); } +|start LIGATURE ref END @/{ REF(ligature_kind,$3); @+ hput_tags($1,TAG(ligature_kind,0)); } +|start DISC ref END @/{ REF(disc_kind,$3); @+ hput_tags($1,TAG(disc_kind,0)); } +|start RULE ref END @/{ REF(rule_kind,$3); @+ hput_tags($1,TAG(rule_kind,0)); } +|start IMAGE ref END @/{ REF(image_kind,$3);@+ hput_tags($1,TAG(image_kind,0)); } +|start LEADERS ref END @/{ REF(leaders_kind,$3); @+ hput_tags($1,TAG(leaders_kind,0)); } +|start BASELINE ref END @/{ REF(baseline_kind,$3);@+ hput_tags($1,TAG(baseline_kind,0)); } +|start LANGUAGE REFERENCE END @/{ REF(language_kind,$3);@+ hput_tags($1,hput_language($3)); }; + +glue_node: start GLUE ref END @/{ REF(glue_kind,$3); +if ($3==zero_skip_no) { hpos=hpos-2; $$=false;@+ } +else {hput_tags($1,TAG(glue_kind,0)); $$=true;@t\2@>@+}}; + +@ + +\getcode +@= +@t\1\kern1em@> +case TAG(penalty_kind,0): HGET_REF(penalty_kind); @+break; +case TAG(kern_kind,b000): HGET_REF(dimen_kind); @+break; +case TAG(kern_kind,b100): hwritef(" !"); @+HGET_REF(dimen_kind); @+break; +case TAG(kern_kind,b001): @| hwritef(" xdimen");@+ HGET_REF(xdimen_kind); @+break; +case TAG(kern_kind,b101): @| hwritef(" ! xdimen");@+ HGET_REF(xdimen_kind); @+break; +case TAG(ligature_kind,0): HGET_REF(ligature_kind); @+break; +case TAG(disc_kind,0): HGET_REF(disc_kind); @+break; +case TAG(glue_kind,0): HGET_REF(glue_kind); @+break; +case TAG(language_kind,b000): HGET_REF(language_kind); @+break; +case TAG(rule_kind,0): HGET_REF(rule_kind); @+break; +case TAG(image_kind,0): HGET_REF(image_kind); @+break; +case TAG(leaders_kind,0): HGET_REF(leaders_kind); @+break; +case TAG(baseline_kind,0): HGET_REF(baseline_kind); @+break; +@ + +@= +#define @[HGET_REF(K)@] {uint8_t n=HGET8;@+ REF(K,n); @+hwrite_ref(n);@+} +@ +\writecode +@= +void hwrite_ref(int n) +{hwritef(" *%d",n);@+} + +void hwrite_ref_node(kind_t k, uint8_t n) +{ hwrite_start(); @+hwritef("%s",content_name[k]);@+ hwrite_ref(n); @+hwrite_end();} +@ + + + +\section{Defaults}\label{defaults}\index{default value} +Several of the predefined values found in the definition section are used +as parameters for the routines borrowed from \TeX\ to display the content +of a \HINT\ file. These values must be defined, but it is inconvenient if +the same standard definitions need to be placed in each and every \HINT\ file. +Therefore we specify in this chapter reasonable default values. +As a consequence, even a \HINT\ file without any definitions should +produce sensible results when displayed. + +The definitions that have default values are integers, dimensions, +extended dimensions, glues, baselines, labels, page templates, +streams, and page ranges. +Each of these defaults has its own subsection below. +Actually the defaults for extended dimensions, baselines, and labels + are not needed by \TeX's routines, but it is nice to have default +values for the extended dimensions that represent +\.{hsize}, \.{vsize}, a zero baseline skip, and a label for the table +of content. + +The array |max_default| contains for each kind-value the maximum number of +the default values. The function |hset_max| is used to initialize them. + +The programs \.{shrink} and \.{stretch} actually do not use the defaults, +but it would be possible to suppress definitions if the defined value +is the same as the default value. +% +We start by setting |max_default[k]==-1|, meaning no defaults, +and |max_fixed[k]==0x10000|, meaning no definitions. +The following subsections will then overwrite these values for +all kinds of definitions that have defaults. +It remains to reset |max_fixed| to $-1$ for all those kinds +that have no defaults but allow definitions. + +@= + for (k=0; k<32; k++) max_default[k]=-1,max_fixed[k]=0x10000; + @/@t}$\hangindent=1em${@>max_fixed[font_kind]= max_fixed[ligature_kind]= max_fixed[disc_kind] + @|=max_fixed[language_kind]=max_fixed[rule_kind]= max_fixed[image_kind] + @|= max_fixed[leaders_kind]= max_fixed[param_kind]=max_fixed[label_kind]@|= -1; +@ + + +\subsection{Integers} +Integers\index{integer} are very simple objects, and it might be tempting not to +use predefined integers at all. But the \TeX\ typesetting engine, +which is used by \HINT, uses many integer parameters to fine tune +its operations. As we will see, all these integer parameters have a predefined +integer number that refers to an integer definition. + +Integers and penalties\index{penalty} share the same kind-value. So a penalty node that references +one of the predefined penalties, simply contains the integer number as a reference +number. + +The following integer numbers are predefined. +The zero integer is fixed with integer number zero. %It is never redefined. +The default values are taken from {\tt plain.tex}. + +@= +typedef enum {@t}$\hangindent=2em${@> + zero_int_no=0, + pretolerance_no=1, + tolerance_no=2, + line_penalty_no=3, + hyphen_penalty_no=4, + ex_hyphen_penalty_no=5, + club_penalty_no=6, + widow_penalty_no=7, + display_widow_penalty_no=8, + broken_penalty_no=9, + pre_display_penalty_no=10, + post_display_penalty_no=11, + inter_line_penalty_no=12, + double_hyphen_demerits_no=13, + final_hyphen_demerits_no=14, + adj_demerits_no=15, + looseness_no=16, + time_no=17, + day_no=18, + month_no=19, + year_no=20, + hang_after_no=21, + floating_penalty_no=22 +} int_no_t; +#define MAX_INT_DEFAULT floating_penalty_no +@ + +@= +max_default[int_kind]=MAX_INT_DEFAULT; +max_fixed[int_kind]=zero_int_no; +int_defaults[zero_int_no]=0; +int_defaults[pretolerance_no]=100; +int_defaults[tolerance_no]=200; +int_defaults[line_penalty_no]=10; +int_defaults[hyphen_penalty_no]=50; +int_defaults[ex_hyphen_penalty_no]=50; +int_defaults[club_penalty_no]=150; +int_defaults[widow_penalty_no]=150; +int_defaults[display_widow_penalty_no]=50; +int_defaults[broken_penalty_no]=100; +int_defaults[pre_display_penalty_no]=10000; +int_defaults[post_display_penalty_no]=0; +int_defaults[inter_line_penalty_no]=0; +int_defaults[double_hyphen_demerits_no]=10000; +int_defaults[final_hyphen_demerits_no]=5000; +int_defaults[adj_demerits_no]=10000; +int_defaults[looseness_no]=0; +int_defaults[time_no]=720; +int_defaults[day_no]=4; +int_defaults[month_no]=7; +int_defaults[year_no]=1776; +int_defaults[hang_after_no]=1; +int_defaults[floating_penalty_no]=20000; +@# + +printf("int32_t int_defaults[MAX_INT_DEFAULT+1]={"); +for (i=0; i<= max_default[int_kind];i++)@/ +{ printf("%d",int_defaults[i]);@+ + if (i= +typedef enum {@t}$\hangindent=2em${@> +zero_dimen_no=0, +hsize_dimen_no=1, +vsize_dimen_no=2, +line_skip_limit_no=3, +max_depth_no=4, +split_max_depth_no=5, +hang_indent_no=6, +emergency_stretch_no=7, +quad_no=8, +math_quad_no=9 +} dimen_no_t; +#define MAX_DIMEN_DEFAULT math_quad_no +@ + +@= +max_default[dimen_kind]=MAX_DIMEN_DEFAULT; +max_fixed[dimen_kind]=zero_dimen_no;@# +dimen_defaults[zero_dimen_no]=0; +dimen_defaults[hsize_dimen_no]=(dimen_t)(6.5*72.27*ONE); +dimen_defaults[vsize_dimen_no]=(dimen_t)(8.9*72.27*ONE); +dimen_defaults[line_skip_limit_no]=0; +dimen_defaults[split_max_depth_no]=(dimen_t)(3.5*ONE); +dimen_defaults[hang_indent_no]=0; +dimen_defaults[emergency_stretch_no]=0; +dimen_defaults[quad_no]=10*ONE; +dimen_defaults[math_quad_no]=10*ONE;@# + +printf("dimen_t dimen_defaults[MAX_DIMEN_DEFAULT+1]={"); +for (i=0; i<= max_default[dimen_kind];i++) +{ printf("0x%x",dimen_defaults[i]); + if (i= +typedef enum { +zero_xdimen_no=0, +hsize_xdimen_no=1, +vsize_xdimen_no=2 +} xdimen_no_t; +#define MAX_XDIMEN_DEFAULT vsize_xdimen_no +@ + +@= +max_default[xdimen_kind]=MAX_XDIMEN_DEFAULT; +max_fixed[xdimen_kind]=vsize_xdimen_no;@# + +printf("xdimen_t xdimen_defaults[MAX_XDIMEN_DEFAULT+1]={"@/ +"{0x0, 0.0, 0.0}, {0x0, 1.0, 0.0}, {0x0, 0.0, 1.0}"@/ +"};\n\n"); +@ + + +\subsection{Glue} + +There are predefined glue\index{glue} numbers that correspond to the skip parameters of \TeX. +The default values are taken from {\tt plain.tex}. + +@= +typedef enum {@t}$\hangindent=2em${@> +zero_skip_no=0, +fil_skip_no=1, +fill_skip_no=2, +line_skip_no=3, +baseline_skip_no=4, +above_display_skip_no=5, +below_display_skip_no=6, +above_display_short_skip_no=7, +below_display_short_skip_no=8, +left_skip_no=9, +right_skip_no=10, +top_skip_no=11, +split_top_skip_no=12, +tab_skip_no=13, +par_fill_skip_no=14 +} glue_no_t; +#define MAX_GLUE_DEFAULT par_fill_skip_no +@ + +@= +max_default[glue_kind]=MAX_GLUE_DEFAULT; +max_fixed[glue_kind]=fill_skip_no; + +glue_defaults[fil_skip_no].p.f=1.0; +glue_defaults[fil_skip_no].p.o=fil_o; + +glue_defaults[fill_skip_no].p.f=1.0; +glue_defaults[fill_skip_no].p.o=fill_o;@# + +glue_defaults[line_skip_no].w.w=1*ONE; +glue_defaults[baseline_skip_no].w.w=12*ONE; + +glue_defaults[above_display_skip_no].w.w=12*ONE; +glue_defaults[above_display_skip_no].p.f=3.0; +glue_defaults[above_display_skip_no].p.o=normal_o; +glue_defaults[above_display_skip_no].m.f=9.0; +glue_defaults[above_display_skip_no].m.o=normal_o; + +glue_defaults[below_display_skip_no].w.w=12*ONE; +glue_defaults[below_display_skip_no].p.f=3.0; +glue_defaults[below_display_skip_no].p.o=normal_o; +glue_defaults[below_display_skip_no].m.f=9.0; +glue_defaults[below_display_skip_no].m.o=normal_o; + +glue_defaults[above_display_short_skip_no].p.f=3.0; +glue_defaults[above_display_short_skip_no].p.o=normal_o; + +glue_defaults[below_display_short_skip_no].w.w=7*ONE; +glue_defaults[below_display_short_skip_no].p.f=3.0; +glue_defaults[below_display_short_skip_no].p.o=normal_o; +glue_defaults[below_display_short_skip_no].m.f=4.0; +glue_defaults[below_display_short_skip_no].m.o=normal_o; + +glue_defaults[top_skip_no].w.w=10*ONE; +glue_defaults[split_top_skip_no].w.w=(dimen_t)8.5*ONE; + +glue_defaults[par_fill_skip_no].p.f=1.0; +glue_defaults[par_fill_skip_no].p.o=fil_o; + +#define @[PRINT_GLUE(G)@] \ + @[printf("{{0x%x, %f, %f},{%f, %d},{%f, %d}}",\ + G.w.w, G.w.h, G.w.v, G.p.f, G.p.o, G.m.f,G.m.o)@]@# + +printf("glue_t glue_defaults[MAX_GLUE_DEFAULT+1]={\n"); +for (i=0; i<= max_default[glue_kind];i++)@/ +{ PRINT_GLUE(glue_defaults[i]); @+ + if (i= +typedef enum {@+ +zero_baseline_no=0@+ +} baseline_no_t; +#define MAX_BASELINE_DEFAULT zero_baseline_no +@ +@= +max_default[baseline_kind]=MAX_BASELINE_DEFAULT; +max_fixed[baseline_kind]=zero_baseline_no;@# +{ baseline_t z={{{0}}}; + printf("baseline_t baseline_defaults[MAX_BASELINE_DEFAULT+1]={{"); + PRINT_GLUE(z.bs); @+printf(", "); @+PRINT_GLUE(z.ls); printf(", 0x%x}};\n\n",z.lsl); +} +@ + +\subsection{Labels} +The zero label\index{label} is predefined. It should point to the +``home'' position of the document which should be the position +where a user can start reading or navigating the document. +For a short document this is usually the start of the document, +and hence, the default is the first position of the content section. +For a larger document, the home position could point to the +table of content where a reader will find links to other parts +of the document. + +@= +typedef enum {@+ +zero_label_no=0@+ +} label_no_t; +#define MAX_LABEL_DEFAULT zero_label_no +@ +@= +max_default[label_kind]=MAX_LABEL_DEFAULT; +printf("label_t label_defaults[MAX_LABEL_DEFAULT+1]="@|"{{0,LABEL_TOP,true,0,0,0}};\n\n"); +@ + + +\subsection{Streams} +The zero stream\index{stream} is predefined for the main content. +@= +typedef enum {@+ +zero_stream_no=0@+ +} stream_no_t; +#define MAX_STREAM_DEFAULT zero_stream_no +@ + +@= +max_default[stream_kind]=MAX_STREAM_DEFAULT; +max_fixed[stream_kind]=zero_stream_no; +@ + + +\subsection{Page Templates} + +The zero page template\index{template} is a predefined, built-in page template. +@= +typedef enum {@+ +zero_page_no=0@+ +} page_no_t; +#define MAX_PAGE_DEFAULT zero_page_no +@ + +@= +max_default[page_kind]=MAX_PAGE_DEFAULT; +max_fixed[page_kind]=zero_page_no; +@ + +\subsection{Page Ranges} + +The page\index{page range} range for the zero page template is +the entire content section. + +@= +typedef enum {@+ +zero_range_no=0@+ +} range_no_t; +#define MAX_RANGE_DEFAULT zero_range_no +@ + +@= +max_default[range_kind]=MAX_RANGE_DEFAULT; +max_fixed[range_kind]=zero_range_no; +@ + + +\section{Content Section} +The content section\index{content section} is just a list of nodes. +Within the \.{shrink} program, +reading a node in long format will trigger writing the node in short format. +Similarly within the \.{stretch} program, reading a node +in short form will cause writing it in long format. As a consequence, +the main task of writing the content section in long format is accomplished +by calling |get_content| and writing it in the short format +is accomplished by parsing the |content_list|. + +%\readcode +\codesection{\redsymbol}{Reading the Long Format}\redindex{1}{6}{Content Section} +\label{content}% +@s CONTENT symbol +@= +%token CONTENT "content" +@ + +@= +::@=content@> :< return CONTENT; >: +@ + + +@= +content_section: START CONTENT { hput_content_start(); } @| content_list END @| + { hput_content_end(); hput_range_defs(); hput_label_defs(); }; +@ + +%\writecode +\codesection{\wrtsymbol}{Writing the Long Format}\wrtindex{1}{6}{Content Section} + +@= +void hwrite_content_section(void) +{ section_no=2; + hwritef("\n"); +} +@ + +%\getcode +\codesection{\getsymbol}{Reading the Short Format}\getindex{1}{6}{Content Section} +@= +void hget_content_section() +{ DBG(DBGBASIC|DBGDIR,"Content\n"); + hget_section(2); + hwrite_range(); + hwrite_label(); + while(hpos= +void hput_content_start(void) +{ DBG(DBGDIR,"Content Section\n"); + section_no=2; + hpos0=hpos=hstart=dir[2].buffer; + hend=hstart+dir[2].bsize; + +} +void hput_content_end(void) +{ + dir[2].size=hpos-hstart; /* Updating the directory entry */ + DBG(DBGDIR,"End Content Section, size=0x%x\n", dir[2].size); +} +@ + + +\section{Processing the Command Line} +The following code explains the command line\index{command line} +parameters and options\index{option}\index{debugging}. +It tells us what to expect in the rest of this section. +{\def\SP{\hskip .5em} +@= + fprintf(stderr, + "Usage: %s [options] filename%s\n",prog_name, in_ext);@/ + fprintf(stderr, + "Options:\n"@/ + "\t -o file\t specify an output file name\n"@/ + "\t -g \t assume global names for auxiliary files\n"@/ + "\t -l \t redirect stderr to a log file\n"@/ + "\t -u \t enable writing utf8 character codes\n"@/ + "\t -x \t enable writing hexadecimal character codes\n"@/ + "\t -c \t enable compression of section 1 and 2\n");@/ +#ifdef DEBUG +fprintf(stderr,"\t -d XXX \t hexadecimal value. OR together these values:\n");@/ +fprintf(stderr,"\t\t\t XX=%03X basic debugging\n", DBGBASIC);@/ +fprintf(stderr,"\t\t\t XX=%03X tag debugging\n", DBGTAGS);@/ +fprintf(stderr,"\t\t\t XX=%03X node debugging\n",DBGNODE);@/ +fprintf(stderr,"\t\t\t XX=%03X definition debugging\n", DBGDEF);@/ +fprintf(stderr,"\t\t\t XX=%03X directory debugging\n", DBGDIR);@/ +fprintf(stderr,"\t\t\t XX=%03X range debugging\n",DBGRANGE);@/ +fprintf(stderr,"\t\t\t XX=%03X float debugging\n", DBGFLOAT);@/ +fprintf(stderr,"\t\t\t XX=%03X compression debugging\n", DBGCOMPRESS);@/ +fprintf(stderr,"\t\t\t XX=%03X buffer debugging\n", DBGBUFFER);@/ +fprintf(stderr,"\t\t\t XX=%03X flex debugging\n", DBGFLEX);@/ +fprintf(stderr,"\t\t\t XX=%03X bison debugging\n", DBGBISON);@/ +fprintf(stderr,"\t\t\t XX=%03X TeX debugging\n", DBGTEX);@/ +fprintf(stderr,"\t\t\t XX=%03X Page debugging\n", DBGPAGE);@/ +fprintf(stderr,"\t\t\t XX=%03X Font debugging\n", DBGFONT);@/ +fprintf(stderr,"\t\t\t XX=%03X Render debugging\n", DBGRENDER);@/ +fprintf(stderr,"\t\t\t XX=%03X Label debugging\n", DBGLABEL);@/ +#endif +@ +} +We define constants for different debug flags. +@= +#define DBGNONE 0x0 +#define DBGBASIC 0x1 +#define DBGTAGS 0x2 +#define DBGNODE 0x4 +#define DBGDEF 0x8 +#define DBGDIR 0x10 +#define DBGRANGE 0x20 +#define DBGFLOAT 0x40 +#define DBGCOMPRESS 0x80 +#define DBGBUFFER 0X100 +#define DBGFLEX 0x200 +#define DBGBISON 0x400 +#define DBGTEX 0x800 +#define DBGPAGE 0x1000 +#define DBGFONT 0x2000 +#define DBGRENDER 0x4000 +#define DBGLABEL 0x8000 +@ + +Next we define common variables that are +needed in all three programs defined here. + +@= +unsigned int debugflags=DBGNONE; +int option_utf8=false; +int option_hex=false; +int option_force=false; +int option_global=false; +int option_compress=false; +char *stem_name=NULL; +int stem_length=0; +@ +The variable |stem_name| contains the name of the input file +not including the extension. The space allocated for it +is large enough to append an extension with up to five characters. +It can be used with the extension {\tt .log} for the log file, +with {\tt .HINT} or {\tt .hnt} for the output file, +and with {\tt .abs} or {\tt .rel} when writing or reading the auxiliary sections. +The {\tt stretch} program will overwrite the |stem_name| +using the name of the output file if it is set with the {\tt -o} +option. + + +Next are the variables that are local in the |main| program. +@= +char *prog_name; +char *in_ext; +char *out_ext; +char *file_name=NULL; +int file_name_length=0; +int option_log=false; +@ + +Processing the command line looks for options and then sets the +input file name\index{file name}. + +@= + debugflags=DBGBASIC; + prog_name=argv[0]; + if (argc < 2) goto explain_usage; + argv++; /* skip the program name */ + while (*argv!=NULL) + { if ((*argv)[0]=='-') + { char option=(*argv)[1]; + switch(option) + { default: goto explain_usage; + case 'o': argv++; + file_name_length=(int)strlen(*argv); + ALLOCATE(file_name,file_name_length+6,char); /*plus extension*/ + strcpy(file_name,*argv);@+ break; + case 'l': option_log=true; @+break; + case 'u': option_utf8=true;@+break; + case 'x': option_hex=true;@+break; + case 'f': option_force=true; @+break; + case 'g': option_global=true; @+break; + case 'c': option_compress=true; @+break; + case 'd': @/ + argv++; if (*argv==NULL) goto explain_usage; + debugflags=strtol(*argv,NULL,16); + break; + } + } + else /* the input file name */ + { int path_length=(int)strlen(*argv); + int ext_length=(int)strlen(in_ext); + ALLOCATE(hin_name,path_length+ext_length+1,char); + strcpy(hin_name,*argv); + if (path_length= +FILE *hin=NULL, *hout=NULL, *hlog=NULL; +@ + + +The log file is opened first because +this is the place where error messages\index{error message} +should go while the other files are opened. +It inherits its name from the input file name. + +@ = +#ifdef DEBUG + if (option_log) + { + strcat(stem_name,".log"); + hlog=freopen(stem_name,"w",stderr); + if (hlog==NULL) + { fprintf(stderr,"Unable to open logfile %s",stem_name); + hlog=stderr; + } + stem_name[stem_length]=0; + } + else + hlog=stderr; +#else + hlog=stderr; +#endif +@ + +Once we have established logging, we can try to open the other files. +@= + hin=fopen(hin_name,"rb"); + if (hin==NULL) QUIT("Unable to open input file %s",hin_name); +@ + +@= + if (file_name!=NULL) + { int ext_length=(int)strlen(out_ext); + if (file_name_length<=ext_length + || strncmp(file_name+file_name_length-ext_length,out_ext,ext_length)!=0) + { strcat(file_name,out_ext); file_name_length+=ext_length; } + } + else + { file_name_length=stem_length+(int)strlen(out_ext); + ALLOCATE(file_name,file_name_length+1,char); + strcpy(file_name,stem_name);@+ + strcpy(file_name+stem_length,out_ext); + } + @@; + hout=fopen(file_name,"wb"); + if (hout==NULL) QUIT("Unable to open output file %s",file_name); +@ + +The {\tt stretch} program will replace the |stem_name| using the stem of the +output file. +@= +stem_length=file_name_length-(int)strlen(out_ext); +ALLOCATE(stem_name,stem_length+6,char); +strncpy(stem_name,file_name,stem_length); +stem_name[stem_length]=0; +@ + +At the very end, we will close the files again. +@= +if (hin_name!=NULL) free(hin_name); +if (hin!=NULL) fclose(hin); +@ +@= +if (file_name!=NULL) free(file_name); +if (hout!=NULL) fclose(hout); +@ +@= +if (hlog!=NULL) fclose(hlog); +if (stem_name!=NULL) free(stem_name); +@ + + + +\section{Error Handling and Debugging}\label{error_section} +There is no good program without good error handling\index{error message}\index{debugging}. +To print messages\index{message} or indicate errors, I define the following macros: +\index{MESSAGE+\.{MESSAGE}}\index{QUIT+\.{QUIT}} + +@(error.h@>= +#ifndef _ERROR_H +#define _ERROR_H +#include +#include +extern FILE *hlog; +extern uint8_t *hpos, *hstart; +#define @[LOG(...)@] @[(fprintf(hlog,__VA_ARGS__),fflush(hlog))@] +#define @[MESSAGE(...)@] @[(fprintf(hlog,__VA_ARGS__),fflush(hlog))@] +#define @[QUIT(...)@] (MESSAGE("ERROR: " __VA_ARGS__),fprintf(hlog,"\n"),exit(1)) + +#endif +@ + + +The amount of debugging\index{debugging} depends on the debugging flags. +For portability, we first define the output specifier for expressions of type |size_t|. +\index{DBG+\.{DBG}}\index{SIZE F+\.{SIZE\_F}}\index{DBGTAG+\.{DBGTAG}} +\index{RNG+\.{RNG}}\index{TAGERR+\.{TAGERR}} +@= +#ifdef WIN32 +#define SIZE_F "0x%x" +#else +#define SIZE_F "0x%zx" +#endif +#ifdef DEBUG +#define @[DBG(FLAGS,...)@] ((debugflags & (FLAGS))?LOG(__VA_ARGS__):0) +#else +#define @[DBG(FLAGS,...)@] 0 +#endif +#define @[DBGTAG(A,P)@] @[DBG(DBGTAGS,@["tag [%s,%d] at " SIZE_F "\n"@],@|NAME(A),INFO(A),(P)-hstart)@] + +#define @[RNG(S,N,A,Z)@] @/\ + if ((int)(N)<(int)(A)||(int)(N)>(int)(Z)) QUIT(S@, " %d out of range [%d - %d]",N,A,Z) + +#define @[TAGERR(A)@] @[QUIT(@["Unknown tag [%s,%d] at " SIZE_F "\n"@],NAME(A),INFO(A),hpos-hstart)@] +@ + +The \.{bison} generated parser will need a function |yyerror| for +error reporting. We can define it now: + +@= +extern int yylineno; +int yyerror(const char *msg) +{ QUIT(" in line %d %s",yylineno,msg); + return 0; +} +@ + +To enable the generation of debugging code \.{bison} needs also the following: +@= +#ifdef DEBUG +#define YYDEBUG 1 +extern int yydebug; +#else +#define YYDEBUG 0 +#endif +@ + + +\appendix + +\section{Traversing Short Format Files}\label{fastforward} +For applications like searching or repositioning a file after reloading +a possibly changed version of a file, it is useful to have a fast way +of getting from one content node to the next. +For quite some nodes, it is possible to know the size of the +node from the tag. So the fastest way to get to the next node +is looking up the node size in a table. + +Other important nodes, for example hbox, vbox, or par nodes, end with a +list node and it is possible to know the size of the node up to the final +list. With that knowledge it is possible to skip the initial +part of the node, then skip the list, and finally skip the tag byte. +The size of the initial part can be stored in the same node size table +using negated values. What works for lists, +of course, will work for other kinds of nodes as well. +So we use the lowest two bits of the values in the size table +to store the number of embedded nodes that follow after the initial part. + +For list nodes neither of these methods works and these nodes can be marked +with a zero entry in the node size table. + +This leads to the following code for a ``fast forward'' function +for |hpos|: + +@= +uint32_t hff_list_pos=0, hff_list_size=0; +uint8_t hff_tag; +void hff_hpos(void) +{ signed char i,k; + hff_tag=*hpos;@+ + DBGTAG(hff_tag,hpos); + i= hnode_size[hff_tag]; + if (i>0) {hpos=hpos+i; @+return;@+ } + else if (i<0) + { k=1+(i&0x3);@+ i=i>>2; + hpos=hpos-i; /* skip initial part */ + while (k>0) + { hff_hpos(); @+k--; @+} /* skip trailing nodes */ + hpos++;/* skip end byte */ + return; + } + else if (hff_tag <=TAG(param_kind,5)) + @@; + TAGERR(hff_tag); +} +@ + + +We will put the |hnode_size| variable into the {\tt hformat.c} file +using the following function. We add some comments and +split negative values into their components, to make the result more +readable. + +@= + printf("signed char hnode_size[0x100]= {\n"); + for (i=0; i<=0xff; i++)@/ + { signed char s = hnode_size[i]; + if (s>=0) printf("%d",s); else printf("-4*%d+%d",-(s>>2),s&3); + if (i<0xff) printf(","); else printf("};"); + if ((i&0x7)==0x7) printf(" /* %s */\n", content_name[KIND(i)]); + } + printf("\n\n"); +@ + +\subsection{Lists}\index{list}\index{text}\index{parameters} +List don't follow the usual schema of nodes. They have a variable size +that is stored in the node. We keep position and size in global variables +so that the list that ends a node can be conveniently located. + +@= +switch (INFO(hff_tag)){ +case 1: hff_list_pos=hpos-hstart+1;hff_list_size=0; hpos=hpos+2;@+ return; +case 2: hpos++;@+ hff_list_size=HGET8;@+ hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+1+1;@+ return; +case 3: hpos++;@+ HGET16(hff_list_size);@+hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+2+1;@+ return; +case 4: hpos++;@+ HGET24(hff_list_size);@+hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+3+1;@+ return; +case 5: hpos++;@+ HGET32(hff_list_size);@+hff_list_pos=hpos-hstart+1; hpos=hpos+1+hff_list_size+1+4+1;@+ return; +} +@ + +Now let's consider the different kinds of nodes. + +\subsection{Glyphs}\index{glyph} +We start with the glyph nodes. All glyph nodes +have a start and an end tag, one byte for the font, +and depending on the info from 1 to 4 bytes for the character code. + +@= +hnode_size[TAG(glyph_kind,1)] = 1+1+1+1; +hnode_size[TAG(glyph_kind,2)] = 1+1+2+1; +hnode_size[TAG(glyph_kind,3)] = 1+1+3+1; +hnode_size[TAG(glyph_kind,4)] = 1+1+4+1; +@ + +\subsection{Penalties}\index{penalty} +Penalty nodes either contain a one byte reference, a one byte number, or a two byte number. + +@= +hnode_size[TAG(penalty_kind,0)] = 1+1+1; +hnode_size[TAG(penalty_kind,1)] = 1+1+1; +hnode_size[TAG(penalty_kind,2)] = 1+2+1; +@ + +\subsection{Kerns}\index{kern} +Kern nodes can contain a reference (either to a dimension or an extended dimension) +a dimension, or an extended dimension node. + +@= +hnode_size[TAG(kern_kind,b000)] = 1+1+1; +hnode_size[TAG(kern_kind,b001)] = 1+1+1; +hnode_size[TAG(kern_kind,b010)] = 1+4+1; +hnode_size[TAG(kern_kind,b011)] = I_T(1,1); +hnode_size[TAG(kern_kind,b100)] = 1+1+1; +hnode_size[TAG(kern_kind,b101)] = 1+1+1; +hnode_size[TAG(kern_kind,b110)] = 1+4+1; +hnode_size[TAG(kern_kind,b111)] = I_T(1,1); +@ + +For the two cases where a kern node contains an extended dimension, +we use the following macro to combine the size of the initial part +with the number of trailing nodes: +@= +#define @[I_T(I,T)@] (((-(I))<<2)|((T)-1)) +@ + +\subsection{Extended Dimensions}\index{extended dimension} +Extended dimensions contain either one two or three 4 byte values depending +on the info bits. +@= +hnode_size[TAG(xdimen_kind,b100)] = 1+4+1; +hnode_size[TAG(xdimen_kind,b010)] = 1+4+1; +hnode_size[TAG(xdimen_kind,b001)] = 1+4+1; +hnode_size[TAG(xdimen_kind,b110)] = 1+4+4+1; +hnode_size[TAG(xdimen_kind,b101)] = 1+4+4+1; +hnode_size[TAG(xdimen_kind,b011)] = 1+4+4+1; +hnode_size[TAG(xdimen_kind,b111)] = 1+4+4+4+1; +@ + +\subsection{Language}\index{language} +Language nodes either code the language in the info value or they contain +a reference byte. +@= +hnode_size[TAG(language_kind,b000)] = 1+1+1; +hnode_size[TAG(language_kind,1)] = 1+1; +hnode_size[TAG(language_kind,2)] = 1+1; +hnode_size[TAG(language_kind,3)] = 1+1; +hnode_size[TAG(language_kind,4)] = 1+1; +hnode_size[TAG(language_kind,5)] = 1+1; +hnode_size[TAG(language_kind,6)] = 1+1; +hnode_size[TAG(language_kind,7)] = 1+1; +@ + +\subsection{Rules}\index{rule} +Rules usually contain a reference, otherwise +they contain either one, two, or three 4 byte values depending +on the info bits. +@= +hnode_size[TAG(rule_kind,b000)] = 1+1+1; +hnode_size[TAG(rule_kind,b100)] = 1+4+1; +hnode_size[TAG(rule_kind,b010)] = 1+4+1; +hnode_size[TAG(rule_kind,b001)] = 1+4+1; +hnode_size[TAG(rule_kind,b110)] = 1+4+4+1; +hnode_size[TAG(rule_kind,b101)] = 1+4+4+1; +hnode_size[TAG(rule_kind,b011)] = 1+4+4+1; +hnode_size[TAG(rule_kind,b111)] = 1+4+4+4+1; +@ + +\subsection{Glue}\index{glue} +Glues usually contain a reference or +they contain either one two or three 4 byte values depending +on the info bits, and possibly even an extended dimension node followed +by two 4 byte values. +@= +hnode_size[TAG(glue_kind,b000)] = 1+1+1; +hnode_size[TAG(glue_kind,b100)] = 1+4+1; +hnode_size[TAG(glue_kind,b010)] = 1+4+1; +hnode_size[TAG(glue_kind,b001)] = 1+4+1; +hnode_size[TAG(glue_kind,b110)] = 1+4+4+1; +hnode_size[TAG(glue_kind,b101)] = 1+4+4+1; +hnode_size[TAG(glue_kind,b011)] = 1+4+4+1; +hnode_size[TAG(glue_kind,b111)] = I_T(1+4+4,1); +@ + + +\subsection{Boxes}\index{box} +The layout of boxes is quite complex and explained in section~\secref{boxnodes}. +All boxes contain height and width, some contain a depth, some a shift amount, +and some a glue setting together with glue sign and glue order. +The last item in a box is a node list. + +@= +hnode_size[TAG(hbox_kind,b000)] = I_T(1+4+4,1); /* tag, height, width*/ +hnode_size[TAG(hbox_kind,b001)] = I_T(1+4+4+4,1); /* and depth */ +hnode_size[TAG(hbox_kind,b010)] = I_T(1+4+4+4,1); /* or shift */ +hnode_size[TAG(hbox_kind,b011)] = I_T(1+4+4+4+4,1); /* or both */ +hnode_size[TAG(hbox_kind,b100)] = I_T(1+4+4+5,1); /* and glue setting*/ +hnode_size[TAG(hbox_kind,b101)] = I_T(1+4+4+4+5,1); /* and depth */ +hnode_size[TAG(hbox_kind,b110)] = I_T(1+4+4+4+5,1); /* or shift */ +hnode_size[TAG(hbox_kind,b111)] = I_T(1+4+4+4+4+5,1); /*or both */ +hnode_size[TAG(vbox_kind,b000)] = I_T(1+4+4,1); /* same for vbox*/ +hnode_size[TAG(vbox_kind,b001)] = I_T(1+4+4+4,1); +hnode_size[TAG(vbox_kind,b010)] = I_T(1+4+4+4,1); +hnode_size[TAG(vbox_kind,b011)] = I_T(1+4+4+4+4,1); +hnode_size[TAG(vbox_kind,b100)] = I_T(1+4+4+5,1); +hnode_size[TAG(vbox_kind,b101)] = I_T(1+4+4+4+5,1); +hnode_size[TAG(vbox_kind,b110)] = I_T(1+4+4+4+5,1); +hnode_size[TAG(vbox_kind,b111)] = I_T(1+4+4+4+4+5,1); +@ + +\subsection{Extended Boxes}\index{extended box} +Extended boxes start with height, width, depth, stretch, or shrink components. +Then follows an extended dimension either as a reference or a node. +The node ends with a list. +@= +hnode_size[TAG(hset_kind,b000)] = I_T(1+4+4+4+4+1,1); +hnode_size[TAG(hset_kind,b001)] = I_T(1+4+4+4+4+4+1,1); +hnode_size[TAG(hset_kind,b010)] = I_T(1+4+4+4+4+4+1,1); +hnode_size[TAG(hset_kind,b011)] = I_T(1+4+4+4+4+4+4+1,1); +hnode_size[TAG(vset_kind,b000)] = I_T(1+4+4+4+4+1,1); +hnode_size[TAG(vset_kind,b001)] = I_T(1+4+4+4+4+4+1,1); +hnode_size[TAG(vset_kind,b010)] = I_T(1+4+4+4+4+4+1,1); +hnode_size[TAG(vset_kind,b011)] = I_T(1+4+4+4+4+4+4+1,1); + +hnode_size[TAG(hset_kind,b100)] = I_T(1+4+4+4+4,2); +hnode_size[TAG(hset_kind,b101)] = I_T(1+4+4+4+4+4,2); +hnode_size[TAG(hset_kind,b110)] = I_T(1+4+4+4+4+4,2); +hnode_size[TAG(hset_kind,b111)] = I_T(1+4+4+4+4+4+4,2); +hnode_size[TAG(vset_kind,b100)] = I_T(1+4+4+4+4,2); +hnode_size[TAG(vset_kind,b101)] = I_T(1+4+4+4+4+4,2); +hnode_size[TAG(vset_kind,b110)] = I_T(1+4+4+4+4+4,2); +hnode_size[TAG(vset_kind,b111)] = I_T(1+4+4+4+4+4+4,2); +@ + +The hpack and vpack nodes start with a shift amount and in case of vpack a depth. +Then again an extended dimension and a list. +@= +hnode_size[TAG(hpack_kind,b000)] = I_T(1+1,1); +hnode_size[TAG(hpack_kind,b001)] = I_T(1+1,1); +hnode_size[TAG(hpack_kind,b010)] = I_T(1+4+1,1); +hnode_size[TAG(hpack_kind,b011)] = I_T(1+4+1,1); +hnode_size[TAG(vpack_kind,b000)] = I_T(1+4+1,1); +hnode_size[TAG(vpack_kind,b001)] = I_T(1+4+1,1); +hnode_size[TAG(vpack_kind,b010)] = I_T(1+4+4+1,1); +hnode_size[TAG(vpack_kind,b011)] = I_T(1+4+4+1,1); + +hnode_size[TAG(hpack_kind,b100)] = I_T(1,2); +hnode_size[TAG(hpack_kind,b101)] = I_T(1,2); +hnode_size[TAG(hpack_kind,b110)] = I_T(1+4,2); +hnode_size[TAG(hpack_kind,b111)] = I_T(1+4,2); +hnode_size[TAG(vpack_kind,b100)] = I_T(1+4,2); +hnode_size[TAG(vpack_kind,b101)] = I_T(1+4,2); +hnode_size[TAG(vpack_kind,b110)] = I_T(1+4+4,2); +hnode_size[TAG(vpack_kind,b111)] = I_T(1+4+4,2); +@ + +\subsection{Leaders}\index{leaders} +Most leader nodes will use a reference. +Otherwise they contain a glue node followed by a box or rule node. +@= +hnode_size[TAG(leaders_kind,b000)] = 1+1+1; +hnode_size[TAG(leaders_kind,1)] = I_T(1,1); +hnode_size[TAG(leaders_kind,2)] = I_T(1,1); +hnode_size[TAG(leaders_kind,3)] = I_T(1,1); +hnode_size[TAG(leaders_kind,b100|1)] = I_T(1,2); +hnode_size[TAG(leaders_kind,b100|2)] = I_T(1,2); +hnode_size[TAG(leaders_kind,b100|3)] = I_T(1,2); +@ + +\subsection{Baseline Skips}\index{baseline skip} +Here we expect either a reference or two optional glue nodes followed by an optional dimension. +@= +hnode_size[TAG(baseline_kind,b000)] = 1+1+1; +hnode_size[TAG(baseline_kind,b001)] = 1+4+1; +hnode_size[TAG(baseline_kind,b010)] = I_T(1,1); +hnode_size[TAG(baseline_kind,b100)] = I_T(1,1); +hnode_size[TAG(baseline_kind,b110)] = I_T(1,2); + +hnode_size[TAG(baseline_kind,b011)] = I_T(1+4,1); +hnode_size[TAG(baseline_kind,b101)] = I_T(1+4,1); +hnode_size[TAG(baseline_kind,b111)] = I_T(1+4,2); +@ + + +\subsection{Ligatures}\index{ligature} +As usual a reference is possible, otherwise the font is followed by character bytes +as given by the info. Only if the info value is 7, the number of character bytes +is stored separately. +@= +hnode_size[TAG(ligature_kind,b000)] = 1+1+1; +hnode_size[TAG(ligature_kind,1)] = 1+1+1+1; +hnode_size[TAG(ligature_kind,2)] = 1+1+2+1; +hnode_size[TAG(ligature_kind,3)] = 1+1+3+1; +hnode_size[TAG(ligature_kind,4)] = 1+1+4+1; +hnode_size[TAG(ligature_kind,5)] = 1+1+5+1; +hnode_size[TAG(ligature_kind,6)] = 1+1+6+1; +hnode_size[TAG(ligature_kind,7)] = I_T(1+1,1); +@ + +\subsection{Discretionary breaks}\index{discretionary break} +The simple cases here are references, discretionary breaks +with empty pre- and post-list, or with a zero line skip limit +Otherwise one or two lists are followed by an optional replace count. + +@= +hnode_size[TAG(disc_kind,b000)] = 1+1+1; +hnode_size[TAG(disc_kind,b010)] = I_T(1,1); +hnode_size[TAG(disc_kind,b011)] = I_T(1,2); +hnode_size[TAG(disc_kind,b100)] = 1+1+1; +hnode_size[TAG(disc_kind,b110)] = I_T(1+1,1); +hnode_size[TAG(disc_kind,b111)] = I_T(1+1,2); +@ + +\subsection{Paragraphs}\index{paragraph} +Paragraph nodes contain an extended dimension, an parameter list and a list. +The first two can be given as a reference. +@= +hnode_size[TAG(par_kind,b000)] = I_T(1+1+1,1); +hnode_size[TAG(par_kind,b010)] = I_T(1+1,2); +hnode_size[TAG(par_kind,b110)] = I_T(1,3); +hnode_size[TAG(par_kind,b100)] = I_T(1+1,2); +@ + +\subsection{Mathematics}\index{mathematics}\index{displayed formula} +Displayed math needs a parameter list, either as list or as reference +followed by an optional left or right equation number and a list. +Text math is simpler: the only information is in the info value. +@= +hnode_size[TAG(math_kind,b000)] = I_T(1+1,1); +hnode_size[TAG(math_kind,b001)] = I_T(1+1,2); +hnode_size[TAG(math_kind,b010)] = I_T(1+1,2); +hnode_size[TAG(math_kind,b100)] = I_T(1,2); +hnode_size[TAG(math_kind,b101)] = I_T(1,3); +hnode_size[TAG(math_kind,b110)] = I_T(1,3); +hnode_size[TAG(math_kind,b111)] = 1+1; +hnode_size[TAG(math_kind,b011)] = 1+1; +@ + +\subsection{Adjustments}\index{adjustment} +@= +hnode_size[TAG(adjust_kind,1)] = I_T(1,1); +@ + +\subsection{Tables}\index{alignment} +Tables have an extended dimension either as a node or as a reference followed +by two lists. +@= +hnode_size[TAG(table_kind,b000)] = I_T(1+1,2); +hnode_size[TAG(table_kind,b001)] = I_T(1+1,2); +hnode_size[TAG(table_kind,b010)] = I_T(1+1,2); +hnode_size[TAG(table_kind,b011)] = I_T(1+1,2); +hnode_size[TAG(table_kind,b100)] = I_T(1,3); +hnode_size[TAG(table_kind,b101)] = I_T(1,3); +hnode_size[TAG(table_kind,b110)] = I_T(1,3); +hnode_size[TAG(table_kind,b111)] = I_T(1,3); +@ +Outer item nodes are lists of inner item nodes, inner item nodes are box nodes +followed by an optional span count. +@= +hnode_size[TAG(item_kind,b000)] = I_T(1,1); /* outer */ +hnode_size[TAG(item_kind,1)] = I_T(1,1); /* inner */ +hnode_size[TAG(item_kind,2)] = I_T(1,1); +hnode_size[TAG(item_kind,3)] = I_T(1,1); +hnode_size[TAG(item_kind,4)] = I_T(1,1); +hnode_size[TAG(item_kind,5)] = I_T(1,1); +hnode_size[TAG(item_kind,6)] = I_T(1,1); +hnode_size[TAG(item_kind,6)] = I_T(2,1); +@ + +\subsection{Images}\index{image} +If not given by a reference, images contain a section reference and optional dimensions, stretch, and shrink. +@= +hnode_size[TAG(image_kind,b000)] = 1+1+1; +hnode_size[TAG(image_kind,b100)] = 1+2+1; +hnode_size[TAG(image_kind,b101)] = 1+2+4+4+1; +hnode_size[TAG(image_kind,b110)] = 1+2+4+4+1; +hnode_size[TAG(image_kind,b111)] = 1+2+4+4+4+4+1; +@ + +\subsection{Links}\index{link} +Links contain either a 2 byte or a 1 byte reference. +@= +hnode_size[TAG(link_kind,b000)] = 1+1+1; +hnode_size[TAG(link_kind,b001)] = 1+2+1; +hnode_size[TAG(link_kind,b010)] = 1+1+1; +hnode_size[TAG(link_kind,b011)] = 1+2+1; +@ + +\subsection{Stream Nodes}\index{stream} +After the stream reference follows a parameter list, either as reference +or as a list, and then a content list. +@= +hnode_size[TAG(stream_kind,b000)] = I_T(1+1+1,1); +hnode_size[TAG(stream_kind,b010)] = I_T(1+1,2); +@ + + +\section{Reading Short Format Files Backwards} +This section is not really part of the file format definition, but it +illustrates an important property of the content section in short +format files: it can be read in both directions. This is important +because we want to be able to start at an arbitrary point in the +content and from there move pagewise backward. + +The program {\tt skip}\index{skip+{\tt skip}} described in this +section does just that. As wee see in appendix~\secref{skip}, its +|main| program is almost the same as the |main| program of the program +{\tt stretch} in appendix~\secref{stretchmain}. +The major difference is the removal of an output file +and the replacement of the call to |hwrite_content_section| by +a call to |hteg_content_section|. + +@= +static void hteg_content_section(void) +{ hget_section(2); + hpos=hend; + while(hpos>hstart) + hteg_content_node(); +} +@ + +The functions |hteg_content_section| and |hteg_content_node| above are +reverse versions of the functions |hget_content_section| and +|hget_content_node|. Many such ``reverse functions'' will follow now +and we will consistently use the same naming scheme: replacing +``{\it get\/}`` by ``{\it teg\/}'' or ``{\tt GET}'' by ``{\tt TEG}''. +The {\tt skip} program does not do much input +checking; it will just extract enough information from a content node +to skip a node and ``advance'' or better ``retreat'' to the previous +node. + +@= +static void hteg_content_node(void) +{ @@; + hteg_content(z); + @@; +} + +static void hteg_content(uint8_t z) +{@+ switch (z)@/ + { + @@;@t\1@>@/ + default: + TAGERR(z); + break;@t\2@>@/ + } +} +@ + +The code to skip the end\index{end byte} byte |z| and to check the start\index{start byte} byte |a| is used repeatedly. + +@= + uint8_t a,z; /* the start and the end byte*/ + uint32_t node_pos=hpos-hstart; + if (hpos<=hstart) return; + HTEGTAG(z); +@ + +@= + HTEGTAG(a); + if (a!=z) QUIT(@["Tag mismatch [%s,%d]!=[%s,%d] at " SIZE_F " to 0x%x\n"@],@|NAME(a),INFO(a),NAME(z),INFO(z),@| + hpos-hstart,node_pos-1); +@ + +We replace the ``{\tt GET}'' macros by the following ``{\tt TEG}'' macros: + +@= +#define @[HBACK(X)@] @[((hpos-(X)= +float32_t hteg_float32(void) +{ union {@+float32_t d; @+ uint32_t bits; @+} u; + HTEG32(u.bits); + return u.d; +} +@ + + +\subsection{Extended Dimensions}\index{extended dimension} +\noindent +@= +#define @[HTEG_XDIMEN(I,X)@] \ + if((I)&b001) HTEG32((X).v); \ + if((I)&b010) HTEG32((X).h);\ + if((I)&b100) HTEG32((X).w); +@ + +@= +static void hteg_xdimen_node(xdimen_t *x) +{ @@; + switch(z) + { +#if 0 +/* currently the info value 0 is not supported */ +case TAG(xdimen_kind,b000): /* see section~\secref{reference} */ + {uint8_t n;@+ n=HTEG8;} @+ break; +#endif + case TAG(xdimen_kind,b001): HTEG_XDIMEN(b001,*x);@+break; + case TAG(xdimen_kind,b010): HTEG_XDIMEN(b010,*x);@+break; + case TAG(xdimen_kind,b011): HTEG_XDIMEN(b011,*x);@+break; + case TAG(xdimen_kind,b100): HTEG_XDIMEN(b100,*x);@+break; + case TAG(xdimen_kind,b101): HTEG_XDIMEN(b101,*x);@+break; + case TAG(xdimen_kind,b110): HTEG_XDIMEN(b110,*x);@+break; + case TAG(xdimen_kind,b111): HTEG_XDIMEN(b111,*x);@+break; + default: + QUIT("Extent expected at 0x%x got %s",node_pos,NAME(z)); @+ break; + } +@@; +} +@ + + +\subsection{Stretch and Shrink}\index{stretchability}\index{shrinkability} +\noindent +@= +#define @[HTEG_STRETCH(S)@] { stch_t st; @+ HTEG32(st.u);@+ S.o=st.u&3;@+ st.u&=~3;@+ S.f=st.f; @+} +@ + +\subsection{Glyphs}\index{glyph} +\noindent +@= +#define HTEG_GLYPH(I,G) \ + (G).f=HTEG8; \ + if (I==1) (G).c=HTEG8;\ + else if (I==2) HTEG16((G).c);\ + else if (I==3) HTEG24((G).c);\ + else if (I==4) HTEG32((G).c); +@ + +@= +@t\1\kern1em@>case TAG(glyph_kind,1): @+{@+glyph_t g;@+ HTEG_GLYPH(1,g);@+}@+break; +case TAG(glyph_kind,2): @+{@+glyph_t g;@+ HTEG_GLYPH(2,g);@+}@+break; +case TAG(glyph_kind,3): @+{@+glyph_t g;@+ HTEG_GLYPH(3,g);@+}@+break; +case TAG(glyph_kind,4): @+{@+glyph_t g;@+ HTEG_GLYPH(4,g);@+}@+break; +@ + + +\subsection{Penalties}\index{penalty} +\noindent +@= +#define @[HTEG_PENALTY(I,P)@] \ +if (I==1) {int8_t n; @+n=HTEG8; @+P=n;@+ } \ +else {int16_t n;@+ HTEG16(n); @+ P=n; @+}\ +@ + +@= +@t\1\kern1em@>case TAG(penalty_kind,1): @+{int32_t p;@+ HTEG_PENALTY(1,p);@+} @+break; +case TAG(penalty_kind,2): @+{int32_t p;@+ HTEG_PENALTY(2,p);@+} @+break; +@ + + +\subsection{Kerns}\index{kern} +\noindent +@= +#define @[HTEG_KERN(I,X)@] @[if (((I)&b011)==2) HTEG32(X.w); else if (((I)&b011)==3) hteg_xdimen_node(&(X))@] +@ + +@= +@t\1\kern1em@>case TAG(kern_kind,b010): @+ {@+xdimen_t x; @+HTEG_KERN(b010,x);@+ } @+break; +case TAG(kern_kind,b011): @+ {@+xdimen_t x; @+HTEG_KERN(b011,x);@+ } @+break; +case TAG(kern_kind,b110): @+ {@+xdimen_t x; @+HTEG_KERN(b110,x);@+ } @+break; +case TAG(kern_kind,b111): @+ {@+xdimen_t x; @+HTEG_KERN(b111,x);@+ } @+break; +@ + +\subsection{Language}\index{language} +\noindent +@= +@t\kern1em@>case TAG(language_kind,1): +case TAG(language_kind,2): +case TAG(language_kind,3): +case TAG(language_kind,4): +case TAG(language_kind,5): +case TAG(language_kind,6): +case TAG(language_kind,7):@+break; +@ + +\subsection{Rules}\index{rule} +\noindent +@= +#define @[HTEG_RULE(I,R)@]@/\ +if ((I)&b001) HTEG32((R).w); @+else (R).w=RUNNING_DIMEN;\ +if ((I)&b010) HTEG32((R).d); @+else (R).d=RUNNING_DIMEN;\ +if ((I)&b100) HTEG32((R).h); @+else (R).h=RUNNING_DIMEN; +@ + +@= +@t\1\kern1em@>case TAG(rule_kind,b011): @+ {rule_t r;@+ HTEG_RULE(b011,r);@+ }@+ break; +case TAG(rule_kind,b101): @+ {rule_t r;@+ HTEG_RULE(b101,r);@+ }@+ break; +case TAG(rule_kind,b001): @+ {rule_t r;@+ HTEG_RULE(b001,r);@+ }@+ break; +case TAG(rule_kind,b110): @+ {rule_t r;@+ HTEG_RULE(b110,r);@+ }@+ break; +case TAG(rule_kind,b111): @+ {rule_t r;@+ HTEG_RULE(b111,r);@+ }@+ break; +@ + +@= +static void hteg_rule_node(void) +{ @@; + if (KIND(z)==rule_kind) { @+rule_t r; @+HTEG_RULE(INFO(z),r); @+} + else + QUIT("Rule expected at 0x%x got %s",node_pos,NAME(z)); + @@; +} +@ +\subsection{Glue}\index{glue} +\noindent +@= +#define @[HTEG_GLUE(I,G)@] @/\ + if(I==b111) hteg_xdimen_node(&((G).w)); else (G).w.h=(G).w.v=0.0;\ + if((I)&b001) HTEG_STRETCH((G).m) @+else (G).m.f=0.0, (G).m.o=0; \ + if((I)&b010) HTEG_STRETCH((G).p) @+else (G).p.f=0.0, (G).p.o=0;\ + if((I)!=b111) { @+if ((I)&b100) HTEG32((G).w.w);@+ else (G).w.w=0;@+ } +@ + +@= +@t\1\kern1em@>case TAG(glue_kind,b001): @+{ glue_t g;@+ HTEG_GLUE(b001,g);@+}@+break; +case TAG(glue_kind,b010): @+{ glue_t g;@+ HTEG_GLUE(b010,g);@+}@+break; +case TAG(glue_kind,b011): @+{ glue_t g;@+ HTEG_GLUE(b011,g);@+}@+break; +case TAG(glue_kind,b100): @+{ glue_t g;@+ HTEG_GLUE(b100,g);@+}@+break; +case TAG(glue_kind,b101): @+{ glue_t g;@+ HTEG_GLUE(b101,g);@+}@+break; +case TAG(glue_kind,b110): @+{ glue_t g;@+ HTEG_GLUE(b110,g);@+}@+break; +case TAG(glue_kind,b111): @+{ glue_t g;@+ HTEG_GLUE(b111,g);@+}@+break; +@ + +@= +static void hteg_glue_node(void) +{ @@; + if (INFO(z)==b000) HTEG_REF(glue_kind); + else + { @+glue_t g; @+HTEG_GLUE(INFO(z),g);@+} + @@; +} +@ + +\subsection{Boxes}\index{box} +\noindent +@= +#define @[HTEG_BOX(I,B)@] \ +hteg_list(&(B.l));\ +if ((I)&b100) @/{ B.s=HTEG8; @+ B.r=hteg_float32();@+ B.o=B.s&0xF; @+B.s=B.s>>4;@+ }\ +else { B.r=0.0;@+ B.o=B.s=0;@+ }\ +if ((I)&b010) HTEG32(B.a); @+else B.a=0;\ +HTEG32(B.w);\ +if ((I)&b001) HTEG32(B.d); @+ else B.d=0;\ +HTEG32(B.h);\ +@ + +@= +@t\1\kern1em@> case TAG(hbox_kind,b000): @+{box_t b; @+HTEG_BOX(b000,b);@+} @+ break; +case TAG(hbox_kind,b001): @+{box_t b; @+HTEG_BOX(b001,b);@+} @+ break; +case TAG(hbox_kind,b010): @+{box_t b; @+HTEG_BOX(b010,b);@+} @+ break; +case TAG(hbox_kind,b011): @+{box_t b; @+HTEG_BOX(b011,b);@+} @+ break; +case TAG(hbox_kind,b100): @+{box_t b; @+HTEG_BOX(b100,b);@+} @+ break; +case TAG(hbox_kind,b101): @+{box_t b; @+HTEG_BOX(b101,b);@+} @+ break; +case TAG(hbox_kind,b110): @+{box_t b; @+HTEG_BOX(b110,b);@+} @+ break; +case TAG(hbox_kind,b111): @+{box_t b; @+HTEG_BOX(b111,b);@+} @+ break; +case TAG(vbox_kind,b000): @+{box_t b; @+HTEG_BOX(b000,b);@+} @+ break; +case TAG(vbox_kind,b001): @+{box_t b; @+HTEG_BOX(b001,b);@+} @+ break; +case TAG(vbox_kind,b010): @+{box_t b; @+HTEG_BOX(b010,b);@+} @+ break; +case TAG(vbox_kind,b011): @+{box_t b; @+HTEG_BOX(b011,b);@+} @+ break; +case TAG(vbox_kind,b100): @+{box_t b; @+HTEG_BOX(b100,b);@+} @+ break; +case TAG(vbox_kind,b101): @+{box_t b; @+HTEG_BOX(b101,b);@+} @+ break; +case TAG(vbox_kind,b110): @+{box_t b; @+HTEG_BOX(b110,b);@+} @+ break; +case TAG(vbox_kind,b111): @+{box_t b; @+HTEG_BOX(b111,b);@+} @+ break; +@ + +@= +static void hteg_hbox_node(void) +{ box_t b; + @@; + if (KIND(z)!=hbox_kind) QUIT("Hbox expected at 0x%x got %s",node_pos,NAME(z)); + HTEG_BOX(INFO(z),b);@/ + @@; +} + +static void hteg_vbox_node(void) +{ box_t b; + @@; + if (KIND(z)!=vbox_kind) QUIT("Vbox expected at 0x%x got %s",node_pos,NAME(z)); + HTEG_BOX(INFO(z),b);@/ + @@; +} +@ + + +\subsection{Extended Boxes}\index{extended box} +\noindent +@= +#define @[HTEG_SET(I)@] @/\ +{ list_t l; @+hteg_list(&l); @+} \ + if ((I)&b100) {xdimen_t x;@+ hteg_xdimen_node(&x); @+} \ + else HTEG_REF(xdimen_kind);\ +{ stretch_t m; @+HTEG_STRETCH(m);@+}\ +{ stretch_t p; @+HTEG_STRETCH(p);@+}\ +if ((I)&b010) { dimen_t a; @+HTEG32(a);@+} \ + { dimen_t w; @+HTEG32(w);@+} \ + { dimen_t d; @+if ((I)&b001) HTEG32(d); @+ else d=0;@+}\ + { dimen_t h; @+HTEG32(h);@+} +@# + +#define @[HTEG_PACK(K,I)@] @/\ + { list_t l; @+hteg_list(&l); @+} \ + if ((I)&b100) {xdimen_t x; hteg_xdimen_node(&x);@+} @+ else HTEG_REF(xdimen_kind);\ + if (K==vpack_kind) { dimen_t d; @+HTEG32(d); @+ }\ + if ((I)&b010) { dimen_t d; @+HTEG32(d); @+ } +@ + +@= +@t\1\kern1em@>case TAG(hset_kind,b000): HTEG_SET(b000); @+ break; +case TAG(hset_kind,b001): HTEG_SET(b001); @+ break; +case TAG(hset_kind,b010): HTEG_SET(b010); @+ break; +case TAG(hset_kind,b011): HTEG_SET(b011); @+ break; +case TAG(hset_kind,b100): HTEG_SET(b100); @+ break; +case TAG(hset_kind,b101): HTEG_SET(b101); @+ break; +case TAG(hset_kind,b110): HTEG_SET(b110); @+ break; +case TAG(hset_kind,b111): HTEG_SET(b111); @+ break;@# + +case TAG(vset_kind,b000): HTEG_SET(b000); @+ break; +case TAG(vset_kind,b001): HTEG_SET(b001); @+ break; +case TAG(vset_kind,b010): HTEG_SET(b010); @+ break; +case TAG(vset_kind,b011): HTEG_SET(b011); @+ break; +case TAG(vset_kind,b100): HTEG_SET(b100); @+ break; +case TAG(vset_kind,b101): HTEG_SET(b101); @+ break; +case TAG(vset_kind,b110): HTEG_SET(b110); @+ break; +case TAG(vset_kind,b111): HTEG_SET(b111); @+ break;@# + +case TAG(hpack_kind,b000): HTEG_PACK(hpack_kind,b000); @+ break; +case TAG(hpack_kind,b001): HTEG_PACK(hpack_kind,b001); @+ break; +case TAG(hpack_kind,b010): HTEG_PACK(hpack_kind,b010); @+ break; +case TAG(hpack_kind,b011): HTEG_PACK(hpack_kind,b011); @+ break; +case TAG(hpack_kind,b100): HTEG_PACK(hpack_kind,b100); @+ break; +case TAG(hpack_kind,b101): HTEG_PACK(hpack_kind,b101); @+ break; +case TAG(hpack_kind,b110): HTEG_PACK(hpack_kind,b110); @+ break; +case TAG(hpack_kind,b111): HTEG_PACK(hpack_kind,b111); @+ break;@# + +case TAG(vpack_kind,b000): HTEG_PACK(vpack_kind,b000); @+ break; +case TAG(vpack_kind,b001): HTEG_PACK(vpack_kind,b001); @+ break; +case TAG(vpack_kind,b010): HTEG_PACK(vpack_kind,b010); @+ break; +case TAG(vpack_kind,b011): HTEG_PACK(vpack_kind,b011); @+ break; +case TAG(vpack_kind,b100): HTEG_PACK(vpack_kind,b100); @+ break; +case TAG(vpack_kind,b101): HTEG_PACK(vpack_kind,b101); @+ break; +case TAG(vpack_kind,b110): HTEG_PACK(vpack_kind,b110); @+ break; +case TAG(vpack_kind,b111): HTEG_PACK(vpack_kind,b111); @+ break; +@ + + +\subsection{Leaders}\index{leaders} +\noindent +@= +#define @[HTEG_LEADERS(I)@]@/ \ +if (KIND(hpos[-1])==rule_kind) hteg_rule_node(); \ +else if (KIND(hpos[-1])==hbox_kind) hteg_hbox_node();\ +else hteg_vbox_node();\ +if ((I)&b100) hteg_glue_node(); +@ + +@= +@t\1\kern1em@>case TAG(leaders_kind,1): @+ HTEG_LEADERS(1); @+break; +case TAG(leaders_kind,2): @+ HTEG_LEADERS(2); @+break; +case TAG(leaders_kind,3): @+ HTEG_LEADERS(3); @+break; +case TAG(leaders_kind,b100|1): @+ HTEG_LEADERS(b100|1); @+break; +case TAG(leaders_kind,b100|2): @+ HTEG_LEADERS(b100|2); @+break; +case TAG(leaders_kind,b100|3): @+ HTEG_LEADERS(b100|3); @+break; +@ + +\subsection{Baseline Skips}\index{baseline skip} +\noindent +@= +#define @[HTEG_BASELINE(I,B)@] \ + if((I)&b010) hteg_glue_node(); \ + else {B.ls.p.o=B.ls.m.o=B.ls.w.w=0; @+B.ls.w.h=B.ls.w.v=B.ls.p.f=B.ls.m.f=0.0;@+}\ + if((I)&b100) hteg_glue_node(); \ + else {B.bs.p.o=B.bs.m.o=B.bs.w.w=0; @+B.bs.w.h=B.bs.w.v=B.bs.p.f=B.bs.m.f=0.0;@+}\ + if((I)&b001) HTEG32((B).lsl); @+else B.lsl=0; +@ + +@= +@t\1\kern1em@>case TAG(baseline_kind,b001): @+{ baseline_t b;@+ HTEG_BASELINE(b001,b);@+ }@+break; +case TAG(baseline_kind,b010): @+{ baseline_t b;@+ HTEG_BASELINE(b010,b);@+ }@+break; +case TAG(baseline_kind,b011): @+{ baseline_t b;@+ HTEG_BASELINE(b011,b);@+ }@+break; +case TAG(baseline_kind,b100): @+{ baseline_t b;@+ HTEG_BASELINE(b100,b);@+ }@+break; +case TAG(baseline_kind,b101): @+{ baseline_t b;@+ HTEG_BASELINE(b101,b);@+ }@+break; +case TAG(baseline_kind,b110): @+{ baseline_t b;@+ HTEG_BASELINE(b110,b);@+ }@+break; +case TAG(baseline_kind,b111): @+{ baseline_t b;@+ HTEG_BASELINE(b111,b);@+ }@+break; +@ +\subsection{Ligatures}\index{ligature} +\noindent +@= +#define @[HTEG_LIG(I,L)@] @/\ +if ((I)==7) hteg_list(&((L).l)); \ +else {(L).l.s=(I); @+hpos-=(L).l.s; @+ (L).l.p=hpos-hstart;@+} \ +(L).f=HTEG8; +@ + +@= +@t\1\kern1em@>case TAG(ligature_kind,1):@+ {lig_t l; @+HTEG_LIG(1,l);@+} @+break; +case TAG(ligature_kind,2):@+ {lig_t l; @+HTEG_LIG(2,l);@+} @+break; +case TAG(ligature_kind,3):@+ {lig_t l; @+HTEG_LIG(3,l);@+} @+break; +case TAG(ligature_kind,4):@+ {lig_t l; @+HTEG_LIG(4,l);@+} @+break; +case TAG(ligature_kind,5):@+ {lig_t l; @+HTEG_LIG(5,l);@+} @+break; +case TAG(ligature_kind,6):@+ {lig_t l; @+HTEG_LIG(6,l);@+} @+break; +case TAG(ligature_kind,7):@+ {lig_t l; @+HTEG_LIG(7,l);@+} @+break; +@ + + +\subsection{Discretionary breaks}\index{discretionary breaks} +\noindent +@= +#define @[HTEG_DISC(I,H)@]\ +if ((I)&b001) hteg_list(&((H).q)); else { (H).q.p=hpos-hstart; @+(H).q.s=0; @+(H).q.k=list_kind; @+}\ +if ((I)&b010) hteg_list(&((H).p)); else { (H).p.p=hpos-hstart; @+(H).p.s=0; @+(H).p.k=list_kind; @+} \ +if ((I)&b100) (H).r=HTEG8; @+else (H).r=0; +@ +@= +@t\1\kern1em@>case TAG(disc_kind,b001): @+{disc_t h; @+HTEG_DISC(b001,h); @+} @+break; +case TAG(disc_kind,b010): @+{disc_t h; @+HTEG_DISC(b010,h); @+} @+break; +case TAG(disc_kind,b011): @+{disc_t h; @+HTEG_DISC(b011,h); @+} @+break; +case TAG(disc_kind,b100): @+{disc_t h; @+HTEG_DISC(b100,h); @+} @+break; +case TAG(disc_kind,b101): @+{disc_t h; @+HTEG_DISC(b101,h); @+} @+break; +case TAG(disc_kind,b110): @+{disc_t h; @+HTEG_DISC(b110,h); @+} @+break; +case TAG(disc_kind,b111): @+{disc_t h; @+HTEG_DISC(b111,h); @+} @+break; +@ + + +\subsection{Paragraphs}\index{paragraph} +\noindent +@= +#define @[HTEG_PAR(I)@] @/\ + { list_t l; @+hteg_list(&l); @+} \ + if ((I)&b010) { list_t l; @+hteg_param_list(&l); @+} else if ((I)!=b100) HTEG_REF(param_kind);\ + if ((I)&b100) {xdimen_t x; @+ hteg_xdimen_node(&x); @+} else HTEG_REF(xdimen_kind);\ + if ((I)==b100) HTEG_REF(param_kind); +@ + +@= +@t\1\kern1em@>case TAG(par_kind,b000): @+HTEG_PAR(b000);@+break; +case TAG(par_kind,b010): @+HTEG_PAR(b010);@+break; +case TAG(par_kind,b100): @+HTEG_PAR(b100);@+break; +case TAG(par_kind,b110): @+HTEG_PAR(b110);@+break; +@ + + +\subsection{Mathematics}\index{mathematics}\index{displayed formula}% +\noindent +@= +#define @[HTEG_MATH(I)@] \ +if ((I)&b001) hteg_hbox_node();\ +{ list_t l; @+hteg_list(&l); @+} \ +if ((I)&b010) hteg_hbox_node(); \ +if ((I)&b100) { list_t l; @+hteg_param_list(&l); @+} @+ else HTEG_REF(param_kind); +@ + +@= +@t\1\kern1em@>case TAG(math_kind,b000): HTEG_MATH(b000); @+ break; +case TAG(math_kind,b001): HTEG_MATH(b001); @+ break; +case TAG(math_kind,b010): HTEG_MATH(b010); @+ break; +case TAG(math_kind,b100): HTEG_MATH(b100); @+ break; +case TAG(math_kind,b101): HTEG_MATH(b101); @+ break; +case TAG(math_kind,b110): HTEG_MATH(b110); @+ break; +case TAG(math_kind,b011): +case TAG(math_kind,b111): @+ break; +@ + +\subsection{Images}\index{image} +\noindent +@= +#define @[HTEG_IMAGE(I,X)@] @/\ +if (I&b001) {HTEG_STRETCH((X).m);HTEG_STRETCH((X).p);@+}\ +else { (X).p.f=(X).m.f=0.0; (X).p.o=(X).m.o=normal_o;@+}\ +if (I&b010) {HTEG32((X).h);HTEG32((X).w);@+} \ +else (X).w=(X).h=0;\ +HTEG16((X).n); +@ + +@= +@t\1\kern1em@>case TAG(image_kind,b100): @+ { image_t x;@+HTEG_IMAGE(b100,x);@+}@+break; +case TAG(image_kind,b101): @+ { image_t x;@+HTEG_IMAGE(b101,x);@+}@+break; +case TAG(image_kind,b110): @+ { image_t x;@+HTEG_IMAGE(b110,x);@+}@+break; +case TAG(image_kind,b111): @+ { image_t x;@+HTEG_IMAGE(b111,x);@+}@+break; +@ + +\subsection{Links and Labels} +\noindent +@= +#define @[HTEG_LINK(I)@] @/\ +{ uint16_t n; if (I&b001) HTEG16(n);@+ else n=HTEG8; @+} +@ + +@= +@t\1\kern1em@>case TAG(link_kind,b000): @+ HTEG_LINK(b000); @+break; +case TAG(link_kind,b001): @+ HTEG_LINK(b001); @+break; +case TAG(link_kind,b010): @+ HTEG_LINK(b010); @+break; +case TAG(link_kind,b011): @+ HTEG_LINK(b011); @+break; +@ + + +\subsection{Plain Lists, Texts, and Parameter Lists}\index{list} + +\noindent +@= +void hteg_size_boundary(info_t info) +{ uint32_t n; + if (info<2) return; + n=HTEG8; + if (n-1!=0x100-info) QUIT(@["List size boundary byte 0x%x does not match info value %d at " SIZE_F@], + n, info,hpos-hstart); +} + +uint32_t hteg_list_size(info_t info) +{ uint32_t n; + if (info==1) return 0; + else if (info==2) n=HTEG8; + else if (info==3) HTEG16(n); + else if (info==4) HTEG24(n); + else if (info==5) HTEG32(n); + else QUIT("List info %d must be 1, 2, 3, 4, or 5",info); + return n; +} + +void hteg_list(list_t *l) +{ @@, + @+if (KIND(z)!=list_kind && KIND(z)!=text_kind &&@| KIND(z)!=param_kind) @/ + QUIT("List expected at 0x%x", (uint32_t)(hpos-hstart)); + else + { uint32_t s; + l->k=KIND(z); + l->s=hteg_list_size(INFO(z)); + hteg_size_boundary(INFO(z)); + hpos=hpos-l->s; + l->p=hpos-hstart; + hteg_size_boundary(INFO(z)); + s=hteg_list_size(INFO(z)); + if (s!=l->s) QUIT(@["List sizes at " SIZE_F " and 0x%x do not match 0x%x != 0x%x"@], + hpos-hstart,node_pos-1,s,l->s); + @@; + } +} + +void hteg_param_list(list_t *l) +{ @+if (KIND(*(hpos-1))!=param_kind) return; + hteg_list(l); +} + + +@ + +\subsection{Adjustments}\index{adjustment} +\noindent +@= +@t\1\kern1em@>case TAG(adjust_kind,b001): @+ { list_t l; @+hteg_list(&l);@+ } @+ break; +@ + +\subsection{Tables}\index{table} +\noindent +@= +#define @[HTEG_TABLE(I)@] \ +{@+ list_t l; @+ hteg_list(&l);@+}\ +{@+ list_t l; @+ hteg_list(&l);@+}\ +if ((I)&b100) {xdimen_t x;@+ hteg_xdimen_node(&x);@+} else HTEG_REF(xdimen_kind)@; +@ + +@= +@t\1\kern1em@>case TAG(table_kind,b000): @+ HTEG_TABLE(b000); @+ break; +case TAG(table_kind,b001): @+ HTEG_TABLE(b001); @+ break; +case TAG(table_kind,b010): @+ HTEG_TABLE(b010); @+ break; +case TAG(table_kind,b011): @+ HTEG_TABLE(b011); @+ break; +case TAG(table_kind,b100): @+ HTEG_TABLE(b100); @+ break; +case TAG(table_kind,b101): @+ HTEG_TABLE(b101); @+ break; +case TAG(table_kind,b110): @+ HTEG_TABLE(b110); @+ break; +case TAG(table_kind,b111): @+ HTEG_TABLE(b111); @+ break;@# + +case TAG(item_kind,b000): @+{@+ list_t l; @+hteg_list(&l);@+ } @+ break; +case TAG(item_kind,b001): hteg_content_node(); @+ break; +case TAG(item_kind,b010): hteg_content_node(); @+ break; +case TAG(item_kind,b011): hteg_content_node(); @+ break; +case TAG(item_kind,b100): hteg_content_node(); @+ break; +case TAG(item_kind,b101): hteg_content_node(); @+ break; +case TAG(item_kind,b110): hteg_content_node(); @+ break; +case TAG(item_kind,b111): hteg_content_node(); @+{uint8_t n;@+ n=HTEG8;@+}@+ break; +@ + + + +\subsection{Stream Nodes}\index{stream} +@= +#define @[HTEG_STREAM(I)@] @/\ +{ list_t l; @+hteg_list(&l); @+}\ +if ((I)&b010) { list_t l; @+hteg_param_list(&l); @+} @+ else HTEG_REF(param_kind);\ +HTEG_REF(stream_kind); +@ + +@= +@t\1\kern1em@>case TAG(stream_kind,b000): HTEG_STREAM(b000); @+ break; +case TAG(stream_kind,b010): HTEG_STREAM(b010); @+ break; +@ + + + +\subsection{References}\index{reference} +\noindent +@= +#define @[HTEG_REF(K)@] do@+{uint8_t n; @+ n=HTEG8;@+} @+ while (false) +@ + +@= +@t\1\kern1em@>case TAG(penalty_kind,0): HTEG_REF(penalty_kind); @+break; +case TAG(kern_kind,b000): HTEG_REF(dimen_kind); @+break; +case TAG(kern_kind,b100): HTEG_REF(dimen_kind); @+break; +case TAG(kern_kind,b001): HTEG_REF(xdimen_kind); @+break; +case TAG(kern_kind,b101): HTEG_REF(xdimen_kind); @+break; +case TAG(ligature_kind,0): HTEG_REF(ligature_kind); @+break; +case TAG(disc_kind,0): HTEG_REF(disc_kind); @+break; +case TAG(glue_kind,0): HTEG_REF(glue_kind); @+break; +case TAG(language_kind,0): HTEG_REF(language_kind); @+break; +case TAG(rule_kind,0): HTEG_REF(rule_kind); @+break; +case TAG(image_kind,0): HTEG_REF(image_kind); @+break; +case TAG(leaders_kind,0): HTEG_REF(leaders_kind); @+break; +case TAG(baseline_kind,0): HTEG_REF(baseline_kind); @+break; +@ + + +\section{Code and Header Files}\index{code file}\index{header file} + +\subsection{{\tt basetypes.h}} +To define basic types in a portable way, we create an include file. +The macro |_MSC_VER| (Microsoft Visual C Version)\index{Microsoft Visual C} +is defined only if using the respective compiler. +\index{false+\\{false}}\index{true+\\{true}}\index{bool+\&{bool}} +@(basetypes.h@>= +#ifndef __BASETYPES_H__ +#define __BASETYPES_H__ +#include +#include +#ifndef _STDLIB_H +#define _STDLIB_H +#endif +#ifdef _MSC_VER +#include +#define uint8_t UINT8 +#define uint16_t UINT16 +#define uint32_t UINT32 +#define uint64_t UINT64 +#define int8_t INT8 +#define int16_t INT16 +#define int32_t INT32 +#define bool BOOL +#define true (0==0) +#define false (!true) +#define __SIZEOF_FLOAT__ 4 +#define __SIZEOF_DOUBLE__ 8 +#define INT32_MAX (2147483647) +#define PRIx64 "I64x" +#pragma @[warning( disable : @[4244@]@t @> @[4996@]@t @> @[4127@])@] +#else +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#endif +typedef float float32_t; +typedef double float64_t; +#if __SIZEOF_FLOAT__!=4 +#error @=float32 type must have size 4@> +#endif +#if __SIZEOF_DOUBLE__!=8 +#error @=float64 type must have size 8@> +#endif +#endif +@ + + + +\subsection{{\tt hformat.h}}\index{hformat.h+{\tt hformat.h}} +The \.{hformat.h} file contains definitions of types, macros, variables and functions +that are needed in other compilation units. + +@(hformat.h@>= +#ifndef _HFORMAT_H_ +#define _HFORMAT_H_ +@@; +@@; +@@; +@@; +@@; + +extern const char *content_name[32]; +extern const char *definition_name[32]; +extern unsigned int debugflags; +extern FILE *hlog; +extern int max_fixed[32], max_default[32], max_ref[32], max_outline; +extern int32_t int_defaults[MAX_INT_DEFAULT+1]; +extern dimen_t dimen_defaults[MAX_DIMEN_DEFAULT+1]; +extern xdimen_t xdimen_defaults[MAX_XDIMEN_DEFAULT+1]; +extern glue_t glue_defaults[MAX_GLUE_DEFAULT+1]; +extern baseline_t baseline_defaults[MAX_BASELINE_DEFAULT+1]; +extern label_t label_defaults[MAX_LABEL_DEFAULT+1]; +extern signed char hnode_size[0x100]; + +#endif +@ +\subsection{{\tt hformat.c}}\index{hformat.c+{\tt hformat.c}}\index{mkhformat.c+{\tt mkhformat.c}} +For maximum flexibility and efficiency, the file {\tt hformat.c} +is generated by a \CEE\ program. +Here is the |main| program of {\tt mkhformat}: + +@(mkhformat.c@>= +#include +#include "basetypes.h" +#include "hformat.h" +@@; + +int max_fixed[32], max_default[32]; + +int32_t int_defaults[MAX_INT_DEFAULT+1]={0}; +dimen_t dimen_defaults[MAX_DIMEN_DEFAULT+1]={0}; +xdimen_t xdimen_defaults[MAX_XDIMEN_DEFAULT+1]={{0}}; +glue_t glue_defaults[MAX_GLUE_DEFAULT+1]={{{0}}}; +baseline_t baseline_defaults[MAX_BASELINE_DEFAULT+1]={{{{0}}}}; + +signed char hnode_size[0x100]={0}; +@@; +int main(void) +{ kind_t k; + int i; + + + printf("#include \"basetypes.h\"\n"@/ + "#include \"hformat.h\"\n\n");@/ + + @@; + + printf("int max_outline=-1;\n\n"); + + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + + @@; + @@; + return 0; +} +@ + +The following code prints the arrays containing the default values. + +@= + printf("int max_fixed[32]= {"); + for (k=0; k<32; k++)@/ + { printf("%d",max_fixed[k]);@+ + if (k<31) printf(", ");@+ + } + printf("};\n\n");@# + printf("int max_default[32]= {"); + for (k=0; k<32; k++)@/ + { printf("%d",max_default[k]);@+ + if (k<31) printf(", ");@+ + } + printf("};\n\n"); + printf("int max_ref[32]= {"); + for (k=0; k<32; k++)@/ + { printf("%d",max_default[k]);@+ + if (k<31) printf(", ");@+ + } + printf("};\n\n"); +@ + + +\subsection{{\tt hget.h}}\index{hget.h+{\tt hget.h}} +The \.{hget.h} file contains function prototypes for all the functions +that read the short format. + +@(hget.h@>= +@@; +@@; +@@; + +extern entry_t *dir; +extern uint16_t section_no, max_section_no; +extern uint8_t *hpos, *hstart, *hend, *hpos0; +extern uint64_t hin_size, hin_time; +extern uint8_t *hin_addr; + +extern label_t *labels; +extern char *hin_name; +extern bool hget_map(void); +extern void hget_unmap(void); + +extern void new_directory(uint32_t entries); +extern void hset_entry(entry_t *e, uint16_t i, @|uint32_t size, uint32_t xsize, char *file_name); + +extern void hget_banner(void); +extern void hget_section(uint16_t n); +extern void hget_entry(entry_t *e); +extern void hget_directory(void); +extern void hclear_dir(void); +extern bool hcheck_banner(char *magic); + +extern void hget_max_definitions(void); +extern uint32_t hget_utf8(void); +extern void hget_size_boundary(info_t info); +extern uint32_t hget_list_size(info_t info); +extern void hget_list(list_t *l); +extern uint32_t hget_utf8(void); +extern float32_t hget_float32(void); +extern float32_t hteg_float32(void); +extern void hteg_size_boundary(info_t info); +extern uint32_t hteg_list_size(info_t info); +extern void hteg_list(list_t *l); +extern void hff_hpos(void); +extern uint32_t hff_list_pos, hff_list_size; +extern uint8_t hff_tag; +@ + + + +\subsection{{\tt hget.c}}\index{hget.c+{\tt hget.c}} +@(hget.c@>= +#include "basetypes.h" +#include +#include +#include +#include +#include +#include + +#include "error.h" +#include "hformat.h" +#include "hget.h" + +@@; + +@@; +@@; +@@; + +@@; +@@; +@@; +@ + +\subsection{{\tt hput.h}}\index{hput.h+{\tt hput.h}} +The \.{hput.h} file contains function prototypes for all the functions +that write the short format. + + +@(hput.h@>= +@@; +@@; +@@; +@@; +extern entry_t *dir; +extern uint16_t section_no, max_section_no; +extern uint8_t *hpos, *hstart, *hend, *hpos0; +extern int next_range; +extern range_pos_t *range_pos; +extern int *page_on; +extern label_t *labels; +extern int first_label; +extern int max_outline; +extern outline_t *outlines; + + +extern FILE *hout; +extern void new_directory(uint32_t entries); +extern void new_output_buffers(void); + +/* declarations for the parser */ +extern void hput_definitions_start(void); +extern void hput_definitions_end(void); +extern void hput_content_start(void); +extern void hput_content_end(void); + +extern void hset_label(int n,int w); +extern uint8_t hput_link(int n, int on); +extern void hset_outline(int m, int r, int d, uint32_t p); +extern void hput_label_defs(void); + +extern void hput_tags(uint32_t pos, uint8_t tag); +extern uint8_t hput_glyph(glyph_t *g); +extern uint8_t hput_xdimen(xdimen_t *x); +extern uint8_t hput_int(int32_t p); +extern uint8_t hput_language(uint8_t n); +extern uint8_t hput_rule(rule_t *r); +extern uint8_t hput_glue(glue_t *g); +extern uint8_t hput_list(uint32_t size_pos, list_t *y); +extern uint8_t hsize_bytes(uint32_t n); +extern void hput_txt_cc(uint32_t c); +extern void hput_txt_font(uint8_t f); +extern void hput_txt_global(ref_t *d); +extern void hput_txt_local(uint8_t n); +extern info_t hput_box_dimen(dimen_t h, dimen_t d, dimen_t w); +extern info_t hput_box_shift(dimen_t a); +extern info_t hput_box_glue_set(int8_t s, float32_t r, order_t o); +extern void hput_stretch(stretch_t *s); +extern uint8_t hput_kern(kern_t *k); +extern void hput_utf8(uint32_t c); +extern uint8_t hput_ligature(lig_t *l); +extern uint8_t hput_disc(disc_t *h); +extern info_t hput_span_count(uint32_t n); +extern uint8_t hput_image(image_t *x); +extern void hput_string(char *str); +extern void hput_range(uint8_t pg, bool on); +extern void hput_max_definitions(void); +extern uint8_t hput_dimen(dimen_t d); +extern uint8_t hput_font_head(uint8_t f, char *n, dimen_t s,@| uint16_t m, uint16_t y); +extern void hput_range_defs(void); +extern void hput_xdimen_node(xdimen_t *x); +extern void hput_directory(void); +extern void hput_hint(char * str); +extern void hput_list_size(uint32_t n, int i); +extern int hcompress_depth(int n, int c); +@ + + +\subsection{{\tt hput.c}}\label{writeshort}\index{hput.c+{\tt hput.c}} +\noindent +@(hput.c@>= +#include "basetypes.h" +#include +#include +#include +#include +#include +#include "error.h" +#include "hformat.h" +#include "hput.h" + +@@; +@@; +@@; +@@; +@@; +@ + +\subsection{{\tt shrink.l}}\index{shrink.l+{\tt shrink.l}}\index{scanning} +The definitions for lex are collected in the file {\tt shrink.l} + +@(shrink.lex-in@>= +%{ +#include "basetypes.h" +#include "error.h" +#include "hformat.h" +#include "hput.h" + +@@; +#include "shrink-parser.h" + +@@;@+ +@@; +int yywrap (void )@+{ return 1;@+} +#ifdef _MSC_VER +#pragma warning( disable : 4267) +#endif +%} + +%option yylineno batch stack +%option debug +%option nounistd nounput noinput noyy_top_state + +@@/ + +%% + +@@/ +::@=[a-z]+@> :< QUIT("Unexpected keyword '%s' in line %d",@|yytext,yylineno); >: +::@=.@> :< QUIT("Unexpected character '%c' (0x%02X) in line %d",@|yytext[0]>' '?yytext[0]:' ',yytext[0],yylineno); >: + +%% +@ + + + +\subsection{{\tt shrink.y}}\index{shrink.y+{\tt shrink.y}}\index{parsing} + +The grammar rules for bison are collected in the file {\tt shrink.y}. +% for the option %token-table use the command line parameter -k + + +@(shrink.yacc-in@>= +%{ +#include "basetypes.h" +#include +#include +#include "error.h" +#include "hformat.h" +#include "hput.h" +extern char **hfont_name; /* in common variables */ + +@@; + +extern void hset_entry(entry_t *e, uint16_t i, @|uint32_t size, + uint32_t xsize, char *file_name); + +@@; +extern int yylex(void); + +@@; + +%} + + +@t{\label{union}\index{union}\index{parsing}}@> + + +%union {uint32_t u; @+ int32_t i; @+ char *s; @+ float64_t f; @+ glyph_t c; + @+ dimen_t @+d; stretch_t st; @+ xdimen_t xd; @+ kern_t kt; + @+ rule_t r; @+ glue_t g; @+ @+ image_t x; + @+ list_t l; @+ box_t h; @+ disc_t dc; @+ lig_t lg; + @+ ref_t rf; @+ info_t info; @+ order_t o; bool@+ b; + } + +@t{}@> + +%error_verbose +%start hint +@t@> +@@/ +%% +@@; +%% +@ + +\subsection{{\tt hishrink.c}}\index{hishrink.c+{\tt hishrink.c}} + +\.{shrink} is a \CEE\ program translating a \HINT\ file in long format into a \HINT\ file in short format. + +@(hishrink.c@>= +#include "basetypes.h" +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include + +#include "error.h" +#include "hformat.h" +#include "hput.h" +#include "shrink-parser.h" + +extern void yyset_debug(int lex_debug); +extern int yylineno; +extern FILE *yyin, *yyout; +extern int yyparse(void); + +@@; + +@@; +@@; +@@; +@@; +@@; +@@; + +int main(int argc, char *argv[]) +{ @@; + in_ext=".HINT"; + out_ext=".hnt"; + @@; + + if (debugflags&DBGFLEX) yyset_debug(1); else yyset_debug(0); +#if YYDEBUG + if (debugflags&DBGBISON) yydebug=1; + else yydebug=0; +#endif + @@; + @@; + @@; + + yyin=hin; + yyout=hlog; + @@; + if (!hcheck_banner("HINT")) QUIT("Invalid banner"); + yylineno++; + DBG(DBGBISON|DBGFLEX,"Parsing Input\n"); + yyparse(); + + hput_directory(); + + hput_hint("shrink"); + + @@; + @@; + @@; + return 0; +explain_usage: + @@; + return 1; +} +@ + + + +\subsection{{\tt histretch.c}}\label{stretchmain}\index{histretch.c+{\tt histretch.c}} +\.{histretch} is a \CEE\ program translating a \HINT\ file in short +format into a \HINT\ file in long format. + +@(histretch.c@>= +#include "basetypes.h" +#include +#include +#include +#include +#include +#include +#ifdef WIN32 +#include +#endif +#include +#include "error.h" +#include "hformat.h" +#include "hget.h" + +@@; +@@; +@@; +@@; +@@; +@@; +@@; +@@; + +@@; +@@; +@@; +@@; +@@; +@@; + +int main(int argc, char *argv[]) +{ @@; + + in_ext=".hnt"; + out_ext=".HINT"; + @@; + @@; + @@; + @@; + if (!hget_map()) QUIT("Unable to map the input file"); + hpos=hstart=hin_addr; + hend=hstart+hin_size; + hget_banner(); + if (!hcheck_banner("hint")) QUIT("Invalid banner"); + hput_banner("HINT","stretch"); + hget_directory(); + hwrite_directory(); + hget_definition_section(); + hwrite_content_section(); + hwrite_aux_files(); + hget_unmap(); + @@; + DBG(DBGBASIC,"End of Program\n"); + @@; + return 0; +explain_usage: + @@; + return 1;} +@ + +In the above program, the get functions call the write functions +and the write functions call some get functions. This requires +function declarations to satisfy the define before use requirement +of \CEE. Some of the necessary function declarations are already +contained in {\tt hget.h}. The remaining declarations are these: + +@= +extern void hget_xdimen_node(xdimen_t *x); +extern void hget_def_node(void); +extern void hget_font_def(uint8_t f); +extern void hget_content_section(void); +extern uint8_t hget_content_node(void); +extern void hget_glue_node(void); +extern void hget_rule_node(void); +extern void hget_hbox_node(void); +extern void hget_vbox_node(void); +extern void hget_param_list(list_t *l); +extern int hget_txt(void); +@ + + +\subsection{{\tt skip.c}}\label{skip}\index{skip.c+{\tt skip.c}} +\.{skip} is a \CEE\ program reading the content section of a \HINT\ file in short format +backwards. + +@(skip.c@>= +#include "basetypes.h" +#include +#include +#include +#include +#include +#include "error.h" +#include "hformat.h" +@@; + +@@; + +@@; +@@; +@@; +@@; +@@; +@@; + +@@; +@@; +@@; +@@; + +int main(int argc, char *argv[]) +{ @@; + in_ext=".hnt"; + out_ext=".bak"; + + @@; + @@; + if (!hget_map()) QUIT("Unable to map the input file"); + hpos=hstart=hin_addr; + hend=hstart+hin_size; + hget_banner(); + if (!hcheck_banner("hint")) QUIT("Invalid banner"); + hget_directory(); + DBG(DBGBASIC,"Skipping Content Section\n"); + hteg_content_section(); + DBG(DBGBASIC,"Fast forward Content Section\n"); + hpos=hstart; + while(hpos0) + { uint8_t *p=hpos,*q; + DBG(DBGTAGS,"Fast forward list at 0x%x, size %d",hff_list_pos,hff_list_size); + hpos=hstart+hff_list_pos; + q=hpos+hff_list_size; + while (hpos@; + return 0; +explain_usage: + @@; + return 1; +} +@ + +As we have seen already in the {\tt stretch} program, a few +function declarations are necessary to satisfy the define before +use requirement of \CEE. + +@= +static void hteg_content_node(void); +static void hteg_content(uint8_t z); +static void hteg_xdimen_node(xdimen_t *x); +static void hteg_list(list_t *l); +static void hteg_param_list(list_t *l); +static float32_t hteg_float32(void); +static void hteg_rule_node(void); +static void hteg_hbox_node(void); +static void hteg_vbox_node(void); +static void hteg_glue_node(void); +@ + +\thecodeindex + +\crosssections + +\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 format.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/hitexdir/hitex.w b/Build/source/texk/web2c/hitexdir/hitex.w new file mode 100644 index 00000000000..b56946e7421 --- /dev/null +++ b/Build/source/texk/web2c/hitexdir/hitex.w @@ -0,0 +1,4753 @@ +% This file is part of HINT +% Copyright 2017-2021 Martin Ruckert +% +% 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 ../hint.sty +\input ../changefile.sty + +%% defining how to display certain C identifiers +@s uint8_t int +@s uint16_t int +@s uint32_t int +@s uint64_t int +@s int8_t int +@s int16_t int +@s int32_t int +@s float64_t int +@s float32_t int +@s scaled int +@s pointer int +@s memory_word int +@s ASCII_code int +@s small_number int +@s info_t int +@s lig_t int +@s disc_t int +@s math_t int +@s glyph_t int +@s glue_t int +@s rule_t int +@s list_t int +@s kind_t int +@s image_t int +@s glue_ord int +@s four_quarters int +@s eight_bits int +@s internal_font_number int +@s explicit normal +@s str_number int +@s dimen_t int +@s loop else +@s kpse_file_format_type int +@s font_t int + + +\makeindex +\maketoc + +%\makefigindex +\titletrue + +\def\lastrevision{${}$Revision: 2485 ${}$} +\def\lastdate{${}$Date: 2021-08-19 19:37:13 +0200 (Thu, 19 Aug 2021) ${}$} +\eject +\input titlepage.tex + + +\frontmatter + +@ + + +\plainsection{Preface} +To be written + +\vskip 1cm +\noindent {\it M\"unchen\hfil\break +August 20, 2018 \hfill Martin Ruckert} + + +\tableofcontent +%\thefigindex + + +\mainmatter + +\section{Introduction}\label{intro} +Hi\TeX\ is a modified version of \TeX\ that replaces the \.{DVI} +(device independent) output format by the \HINT\ format. The \HINT\ +format, described in \cite{MR:format}, was designed with two +objectives: being able to support reflowing pages using the \TeX\ +typesetting engine and making it simple to use it as output format of +the \TeX\ programm. The present book tries to convince its readers +that the latter claim is true. To this end, this book implements a +version of \TeX\ that produces \HINT\ output---to be precise: a short +format \HINT\ file. + +The book is written as a literate program\cite{Knuth:lp} using ``The +\.{CWEB} System of Structured Documentation''\cite{Knuth:cweb}. The +largest part of this program is, of course, \TeX\cite{Knuth:tex} +itself. Therefore a significant part of Hi\TeX\ consists of +modifications of the \TeX\ source code, which itself is written as a +literate program using ``The \.{WEB} System of Structured +Documentation''\cite{Knuth:WEB}. The tiny, but significant, +difference between \.{WEB}\index{WEB+{\tt WEB}} +and \.{CWEB}\index{CWEB+{\tt CWEB}} is the transition from +\Pascal\ to \CEE/ as target language. To bridge the gap between +\.{WEB} and \.{CWEB}, Hi\TeX\ is not based on the original \.{WEB} +implementation of \TeX\ but on the \.{CWEB} implementation of \TeX\ +as described in~\cite{MR:webtocweb} and~\cite{MR:web2w}. +Further, it does not use the plain {\tt ctex.w} translation of {\tt tex.web} +but the {\tt ktex.w} file which extends \TeX\ with + the features of $\epsilon$-\TeX, the file searching +mechanisms of the {\tt kpathsearch} library, and the command line +conventions of \TeX\ Live. + +To accomplish modifications of a \.{CWEB} file, the \.{CWEB} tools +support the use of ``change files''. These change files are lists of +code changes optionally embellished with explanatory text. Each code +change consists of two parts: a literal copy of the original source +code followed by the replacement text. When a \.{CWEB} tool applies +such a change file to a cweb file, it will read both files +sequentially and applies the code changes in the order given: Whenever +it finds a section in the \.{CWEB} file that matches the first part of +the current code change, it will replace it by the second part of the +code change, and advance to the next code change. + +The present book makes an attempt to present these code changes in +``literate programming style'' and had to overcome two obstacles: +First, in a literate program, the exposition determines the order of +appearance of the code sections; and second, the tools that generate +the documentation from a \.{CWEB} file are not able to generate +documentation for a change file. + +The first problem can be solved by using the program {\tt +tie}\cite{kg:tie}\cite{jg:ctie}. It allows splitting a single change +file into multiple change files, and while within each change file, +the order of changes is still determined by the original cweb file, +changes that belong together can be grouped into separate files. + +The second problem is solved by a simple preprocessor, that converts +change files into cweb files which then can be converted into +\TeX. With the help of some modifications of the macros in {\tt +cwebmac.tex} these \TeX\ files are used to present change files in +this book. + +Let's look at an examples to explain and illustrate the presentation +of code changes in this book: the {\tt display\_node.ch} change file. + +\subsection{The Function |display_node|} +It is the purpose of the {\tt display\_node.ch} change file to make +the \TeX\ code contained in the section ``\'' +available to other parts of Hi\TeX\ as a function. +The function is then used to display debugging output. + +This requires the following changes: + +\changestyle{displaynode.tex} + +As you can see, the two parts of a code change are enclosed in square brackets +where the opening top bracket is labeled {\it old\/} or {\it new\/}. +While a full understanding of these code changes requires a look at the original \TeX\ code, +at least they document what was done and why. + +There is still one thing missing before we can use the |display_node| +function in the {\tt hitex.c} file we are about to generate: +we need an |extern| declaration of |display_node|. +In fact, the Hi\TeX\ program contains a whole bunch of functions +that will need access to \TeX's internals and the most readable +and convenient way to gain this access is a header file. + +Generating such a header file can be accomplished by using the {\tt -h} +and {\tt -e} options of the {\tt web2w} program when converting the {\tt WEB} +source of \TeX\ to its {\tt cweb} source. We just need to modify the +process slightly for our purposes. + +{ +\changestyle{types.tex} +} + + +\section{Modifying \TeX}\label{modifyingTeX} +In this section, we explain the different change files that modify \TeX. +Larger changes are accomplished by replacing entire functions. + + +\subsection{The |main| Program} +We add two function calls to \TeX's |main| program: +|hint_open|, and |hint_close|. + +{ +\changestyle{main.tex} +} +The functions to open and close the \HINT\ file follow in +section~\secref{output}. + + +\subsection{The Command Line} +Hi\TeX\ has a few additional command line options that we define next. + +{ +\changestyle{command_line.tex} +} + +Above we used the function |hint_debug_help|. It is defined as: + +@= +#ifdef DEBUG +void hint_debug_help(void) +{ +fprintf(stderr,@/ + "To generate HINT format debug output use the option\n" + " -debug=XX "@/ +@t\qquad@>"\t XX is a hexadecimal value. OR together these values:\n");@/ +fprintf(stderr,"\t\t\t XX=%04X \t basic debugging\n", DBGBASIC);@/ +fprintf(stderr,"\t\t\t XX=%04X \t tag debugging\n", DBGTAGS);@/ +fprintf(stderr,"\t\t\t XX=%04X \t node debugging\n",DBGNODE);@/ +fprintf(stderr,"\t\t\t XX=%04X \t definition debugging\n", DBGDEF);@/ +fprintf(stderr,"\t\t\t XX=%04X \t directory debugging\n", DBGDIR);@/ +fprintf(stderr,"\t\t\t XX=%04X \t range debugging\n",DBGRANGE);@/ +fprintf(stderr,"\t\t\t XX=%04X \t float debugging\n", DBGFLOAT);@/ +fprintf(stderr,"\t\t\t XX=%04X \t compression debugging\n", DBGCOMPRESS);@/ +fprintf(stderr,"\t\t\t XX=%04X \t buffer debugging\n", DBGBUFFER);@/ +fprintf(stderr,"\t\t\t XX=%04X \t TeX debugging\n", DBGTEX);@/ +fprintf(stderr,"\t\t\t XX=%04X \t page debugging\n", DBGPAGE);@/ +fprintf(stderr,"\t\t\t XX=%04X \t font debugging\n", DBGFONT);@/ +exit(0); +} +#endif +@ + + + +\subsection{The Page Builder}\label{pagebuilder} +The point where Hi\TeX\ goes an entirely different path than \TeX\ is the page builder: +Instead of building a page, Hi\TeX\ writes a \HINT\ file. + +{ +\changestyle{build.tex} +} + + + +\subsection{Adding new {\tt whatsit} Nodes} +\TeX\ has a mechanism to extend it: the {\tt whatsit} nodes. +For new concepts like baseline specifications, paragraphs, and boxes with unknown dimensions, +we define now special {\tt whatsit} nodes. The new node definitions are supplemented by +procedures to print them, copy them, delete them, and handle them in various contexts. + +{ +\changestyle{whatsit.tex} +} + +\subsubsection{Freeing} +Because some of the new nodes occur at places where \TeX\ originaly only +handles box nodes, +there are many places in \TeX\ where we can no longer just say |free_node(b, box_node_size)| +to free a (box) node. +Instead, we have to use the more general routine |flush_node_list|. +This leads to a long series of simple replacements: + +{ +\changestyle{free.tex} +} + +\subsubsection{Unpacking} +In the function |unpackage| the pointer |p| might now point to an +hset, vset, hpack or vpack node instead of a vbox or hbox node. +We have to adapte the following test to this new situation. + +{ +\changestyle{unpackage.tex} +} + +We continue to define auxiliar functions to create the new nodes. + +\subsubsection{Creating} + +The following functions create nodes for paragraphs, displayed equations, baseline skips, +hpack nodes, vpack nodes, hset nodes, vset nodes, and image nodes. + +@= +pointer new_graf_node(void) +{ @+ pointer p; + p=get_node(graf_node_size); + type(p)=whatsit_node; + subtype(p)=graf_node; + graf_params(p)=null; + graf_list(p)=null; + return p; +} + + +pointer new_disp_node(void) +{ @+ pointer p; + p=get_node(disp_node_size); + type(p)=whatsit_node; + subtype(p)=disp_node; + display_params(p)=null; + display_formula(p)=null; + display_eqno(p)=null; + return p; +} + +pointer new_baseline_node(pointer bs, pointer ls, scaled lsl) +{ @+pointer p; + p=get_node(baseline_node_size); + type(p)=whatsit_node; + subtype(p)=baseline_node; + baseline_node_no(p)=hget_baseline_no(bs, ls, lsl); + return p; +} + +pointer new_pack_node(void) +{ @+pointer p; + p=get_node(pack_node_size); + type(p)=whatsit_node; + subtype(p)=hpack_node; + width(p)=depth(p)=height(p)=shift_amount(p)=0; + pack_limit(p)=max_dimen; + list_ptr(p)=null; + return p; +} +pointer new_set_node(void) +{ @+pointer p; + p=get_node(set_node_size); + type(p)=whatsit_node; + subtype(p)=hset_node; + width(p)=depth(p)=height(p)=shift_amount(p)=set_stretch(p)=set_shrink(p)=set_extent(p)=0; + list_ptr(p)=null; + return p; +} + + + + +pointer new_image_node( str_number n, char *a, char *e) +{ pointer p; + int i; + char *fn; + int l; + + p=get_node(image_node_size);type(p)=whatsit_node;subtype(p)=image_node; + image_name(p)=n; + image_area(p)=s_no(a); + image_ext(p)=s_no(e); + fn=hfile_name(n,a,e); +#if 0 + fn=kpse_find_tex(fn); +#endif + i=hnew_file_section(fn); +#if 0 + free(fn); +#endif + image_no(p)=i; + image_width(p)=image_height(p)=image_stretch(p)=image_shrink(p)=0; + image_shrink_order(p)=image_stretch_order(p)=normal;@/ + return p; +} + + + +@ + +\subsubsection{Parameter nodes} + +Parameter nodes are added to the current list using the |add_par_node| function. +It should be possible to check the parameter values against those +stored in the definition section and remove the ones that +are unchanged. It would make the parameter lists shorter, saving +some time when setting and restoring them later. +There is probably not much savings in memory space, because +most of the times a reference number is found for the parameter list. +@= + p=get_node(par_node_size); + type(p)=whatsit_node; + subtype(p)=par_node; + par_type(p)=t; + par_number(p)=n; +@ + +@= + if (t==int_type) par_value(p).i=v; + else if (t==dimen_type) par_value(p).sc=v; + else if (t==glue_type) + {@+ par_value(p).i=v;add_glue_ref(par_value(p).i); @+} + else + { free_node(p, par_node_size); + QUIT("Undefined parameter type %d",t); + } +@ + +@= +void add_par_node(uint8_t t, uint8_t n, int v) +{ @+pointer p; + @@; + @@; + link(p)=link(temp_head); + link(temp_head)=p; +} +@ + + +\subsection{Extended Dimensions} +An extended dimension is a linar function of {\tt hsize} and {\tt vsize}, and +whenever \TeX\ works with a dimension, we want it to be able to deal with +an extended dimension. This implies many changes throughout \TeX's sources. +So let's get started. + +{ +\changestyle{xdimen.tex} +} + + + + +\subsection{Hyphenation} +While the breaking of a paragraph into lines must be postponed because +{\tt hsize} is not known, hyphenation should be done as part of Hi\TeX\ +because we want to keep hyphenation out of the viewer. Therefore +Hi\TeX\ will do hyphenation for all words within a paragraph. + +There is a fine point to observe here: \TeX\ will consider a word as +a candidate for automatic hyphenation only if the world ``follows'' after a +glue. (For the exact rules, see the \TeX book\cite{DK:texbook}, Appendix H.) +As a consequence, \TeX\ usually does not submit the first word of a +paragraph to its hyphenation routine. +Viewing paragraphs that start with a lengthy word on a narrow display +therefore often look more unsightly than necessary: the long word sticks out +into the right margin as much as it can. To remedy this situation, +Hi\TeX\ has a ``{\tt -f}'' option. If set Hi\TeX\ will deviate from +\TeX's rules and submit the first word of a paragraph to the hyphenation algorithm. + +The next problem arises from \TeX's multipass approach to line breaking +and the attempt to have Hi\TeX\ choose exactly the same line breaks as +\TeX\ does: +\TeX\ distingishes between discretionary breaks inserted by the author of a +text, and discretionary breaks discovered by the hyphenation routine. +The latter, called here ``automatic'', are used only in pass two and three +of the line breaking routine. + +The change file that follows below contains mostly three types of changes: +\itemize +\item Hi\TeX\ restricts the |replace_count| to seven bit to make room for an +extra bit to mark automatic discretionary breaks. +Assignments to the replace count are therefore replaced by a macro that +protects the automatic bit. +\item The \TeX\ code for ``\'' +is packaged into a function |hyphenate_word|, so that it can be invoked as needed. +\item And calls of the function |line_break| are replaced by calls to |hline_break| +to have a hook for inserting Hi\TeX specific changes. +\enditemize + +{ +\changestyle{disc.tex} +} + +The function |hline_break| follows: + + +@= +void hline_break(int final_widow_penalty) +{@+ bool auto_breaking; /*is node |cur_p| outside a formula?*/ + pointer r, s ; /*miscellaneous nodes of temporary interest*/ + pointer pp; + bool par_shape_fix=false; + bool first_word=true; + if (DBGTEX&debugflags) + { print_ln();print("Before hline_break:\n"); + breadth_max=200; + depth_threshold=200; + show_node_list(link(head));print_ln(); + } + if (dimen_par_hfactor(hsize_code)==0 && dimen_par_vfactor(hsize_code)==0) + { line_break(final_widow_penalty); /* the easy case */ + return; + } + /* Get ready to start line breaking */ + pp=new_graf_node(); + graf_penalty(pp)=final_widow_penalty; + if(par_shape_ptr==null) + graf_extent(pp)=new_xdimen(dimen_par(hsize_code), + dimen_par_hfactor(hsize_code),dimen_par_vfactor(hsize_code)); + else + @@; + link(temp_head)=link(head); + if (is_char_node(tail)) + { tail_append(new_penalty(inf_penalty))@; + tail_append(new_param_glue(par_fill_skip_code)); + } + else if (type(tail)!=whatsit_node || subtype(tail)!=disp_node) + { 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); + } + DBG(DBGTEX,"\nCalling line_break:\n" + "hang_indent=0x%08X hang_after=%d",hang_indent,hang_after); + if (line_skip_limit!=0) + DBG(DBGTEX," line_skip_limit=0x%08X",line_skip_limit); + DBG(DBGTEX," prev_graf=0x%08X",prev_graf); + + init_cur_lang=prev_graf%0200000; + init_l_hyf=prev_graf/020000000; + init_r_hyf=(prev_graf/0200000)%0100; + pop_nest(); + DBG(DBGTEX," prev_graf=0x%08X",prev_graf); + + /* 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; + if (DBGTEX&debugflags) + { print_ln();print("Before hyphenation:\n"); + breadth_max=200; + depth_threshold=200; + show_node_list(link(temp_head));print_ln(); + } + auto_breaking=true; + cur_p=temp_head; + if (option_hyphen_first && is_char_node(link(cur_p))) + { hyphenate_word(); first_word=false; } + cur_p=link(cur_p); + while (cur_p!=null) + { /*Call |try_break| if |cur_p| is a legal breakpoint...*/ + if (is_char_node(cur_p)) + { /* Advance |cur_p| to the node following the present string...*/ + do { + cur_p=link(cur_p); + } while (is_char_node(cur_p)); + if (cur_p==null) goto done5; /* mr: no glue and penalty at the end */ + } + switch (type(cur_p)) + { case whatsit_node: + adv_past(cur_p); + break; + case glue_node: + if (auto_breaking) /* Try to hyphenate the following word*/ + hyphenate_word(); + break; + case ligature_node: + break; + case disc_node: + /* Try to break after a discretionary fragment...*/ + r=replace_count(cur_p);s=link(cur_p); + while (r > 0) + { decr(r);s=link(s); + } + cur_p=s; + goto done5; + case math_node: + auto_breaking=(subtype(cur_p)==after); + break; + default: + break; + } + if (option_hyphen_first && first_word && is_char_node(link(cur_p))) + { hyphenate_word(); first_word=false; } + cur_p=link(cur_p); +done5:; + } + if (DBGTEX&debugflags) + { print_ln();print("After hline_break:\n"); + breadth_max=200; + depth_threshold=200; + show_node_list(link(temp_head));print_ln(); + } + graf_list(pp)=link(temp_head); + /* adding parameter nodes */ + link(temp_head)=null; + + add_par_node(int_type,pretolerance_code,pretolerance); + add_par_node(int_type,tolerance_code,tolerance); + add_par_node(dimen_type,emergency_stretch_code,emergency_stretch); + + add_par_node(int_type,line_penalty_code,line_penalty); + add_par_node(int_type,hyphen_penalty_code,hyphen_penalty); + add_par_node(int_type,ex_hyphen_penalty_code,ex_hyphen_penalty); + add_par_node(int_type,club_penalty_code,club_penalty); + add_par_node(int_type,widow_penalty_code,widow_penalty); + add_par_node(int_type,broken_penalty_code,broken_penalty); + add_par_node(int_type,inter_line_penalty_code,inter_line_penalty); + add_par_node(int_type,double_hyphen_demerits_code,double_hyphen_demerits); + add_par_node(int_type,final_hyphen_demerits_code,final_hyphen_demerits); + add_par_node(int_type,adj_demerits_code,adj_demerits); + add_par_node(int_type,looseness_code,looseness); + + if (par_shape_fix) + { add_par_node(int_type,hang_after_code,0); + add_par_node(dimen_type,hang_indent_code,second_indent); + } + else + { add_par_node(int_type,hang_after_code,hang_after); + add_par_node(dimen_type,hang_indent_code, hang_indent); + } + + add_par_node(dimen_type,line_skip_limit_code,line_skip_limit); + add_par_node(glue_type,line_skip_code,line_skip); + add_par_node(glue_type,baseline_skip_code,baseline_skip); + + add_par_node(glue_type,left_skip_code,left_skip); + add_par_node(glue_type,right_skip_code,right_skip); + add_par_node(glue_type,par_fill_skip_code,par_fill_skip); + + + /* |par_shape| is not yet supported */ + graf_params(pp)=link(temp_head); + link(temp_head)=null; + append_to_vlist(pp); +} +@ + +Currently Hi\TeX\ dos not implement the parshape feature of \TeX. +The implementation of {\tt \BS list} in \LaTeX\ does however depend +on a simple us of parshape where all lines have the same length +and indentation. We cover this special case be using a hanging +indentation and adjusting the paragraph width by the difference +of the normal {\tt \BS hsize} and the given length. + +@= +{ last_special_line= info(par_shape_ptr)-1; + if (last_special_line!=0) + DBG(DBGTEX,"Warning parshape with n=%d not yet implemented",info(par_shape_ptr)); + second_width= mem[par_shape_ptr+2*(last_special_line+1)].sc; + second_indent= mem[par_shape_ptr+2*last_special_line+1].sc; + + graf_extent(pp)=new_xdimen(second_indent+second_width, + par_shape_hfactor,par_shape_vfactor); + second_width=second_width+ round((double)par_shape_hfactor*hhsize/unity + +(double)par_shape_vfactor*hvsize/unity); + par_shape_fix=true; +} +@ + +\subsection{Baseline Skips} + +\TeX\ will automatically insert a baseline skip between two boxes in a +vertical list. The baseline skip is computed to make the distance +between the baselines of the two boxes exactly equal to the value of +{\tt \BS baselineskip}. And if that is not possible, because it would +make the distance between the descenders of the upper box and the +ascenders of the lower box smaller than {\tt \BS lineskiplimit}, it +will insert at least a small amount of glue of size {\tt \BS +lineskip}. As a further complication, if the depth of the upper box +has the special value |ignore_depth| the insertion of a baseline skip +is suppressed. It is also common practice that authors manipulate the +values of {\tt \BS baselineskip}, {\tt \BS lineskiplimit}, {\tt \BS +lineskip}, and {\tt \BS prevdepth} to change the baseline calculations +of \TeX. + +Of course a prerequisite of the whole computation of a baseline skip +is the knowledge of the depth of the upper box and the height of the +lower box. With the new types of boxes introduced by Hi\TeX\ neither +of them might be known. In these cases, the baseline skip computation +must be defered to the viewer by inserting a whatsit node of subtype +|baseline_node|. + + +{ +\changestyle{baseline.tex} +} + + +\subsection{Displayed Formulas} + +\TeX\ enters into math mode when it finds a math shift character ``\$''. +Then it calls |init_math| which checks for an aditional math shift +character to call either + \ or \. +We remove most of the differences from the latter because we will +treat both cases very similar here and will consider the necessary +differences in the \HINT\ viewer. + +When the shift character that terminates math more is encountered, +\TeX\ calls |after_math| which ends with +either \ or \ . +We need to modify the latter, because positioning a displayed +equation usually depends on {\tt hsize}, +and must be postponed until the viewer knows its value. +So all Hi\TeX\ can do in this case is create a new whatsit node +with subtype |disp_node| and +insert it proceeding as if the formula had occured in ordinary math mode. + + +{ +\changestyle{display.tex} +} + + + + +\subsection{Alignments} + + +{ +\changestyle{align.tex} +} + + +\subsection{Inserts} +\TeX\ uses |vpack| to determine the natural height of inserted +material. In Hi\TeX, the function |vpack| will not always return +a |vlist_node| with a fixed height because the computation of the +height might fail. In the latter case, |vpack| will return a |vset_node| +or |vpack_node|. Therfore Hi\TeX\ will not use the |height| field of +a an |ins_node|. + +As a result we have the following changes: + + +{ +\changestyle{insert.tex} +} + + + + +\subsection{Implementing Hi\TeX\ Specific Primitives}\label{primitive} +Now we implement new \TeX\ primitives: +one to include images, a few more to define page teamplates, +and one to test whether the current instance of \TeX\ is +the new Hi\TeX. +To implement a primitive, we call the |primitive| function. +Since it needs the name of the new primitive as a \TeX\ string, +we need to add the strings to the \TeX\ string pool. + +Note that primitives are put into \TeX's memory when a \TeX\ format file is read. +So adding or changing the code below implies regenerating the format files. +To generate a format file run a command like + +{\tt\obeylines +hinitex +**plain \BS dump +} + +\noindent +Then find out where the \kpse\ library looks for format files +(for example by running + +{\tt\obeylines + kpsewhich latex.fmt +} + +\noindent +and then place the new format file in the same directory. + + +{ +\changestyle{primitive.tex} +} + + + + +\section{Hi\TeX} + +\subsection{Images} +The handling of images is an integral part of Hi\TeX. In +section~\secref{imageext} on page~\pageref{imageext} the {\tt image} +primitive was defined. It requires a filename for the image and +allows the specification, of width, height, stretch and shrink of an +image. If either width or height is not given, Hi\TeX\ tries to +extract this information from the image file itself calling the +function |hget_image_information| with the pointer |p| to the image +node as parameter. + +@= +void hget_image_information(pointer p) +{ char *fn; + FILE *f; + if (image_width(p)!=0 && image_height(p)!=0) return; + fn=dir[image_no(p)].file_name; + f=fopen(fn,"rb"); + if (f==NULL) QUIT("Unable to open image file %s.", fn); + MESSAGE("(%s",fn); + img_buf_size=0; + if (!get_BMP_info(f,fn,p)&&!get_PNG_info(f,fn,p)&&!get_JPG_info(f,fn,p)) + QUIT("Unable to obtain width and height information for image %s",fn); + fclose(f); + MESSAGE(" width= %fpt height= %fpt)",image_width(p)/(double)ONE,image_height(p)/(double)ONE); +} +@ + +When we have found the width and height of the stored image, we can +supplement the information given in the \TeX\ file. Occasionaly, the +image file will not specify the absolute dimensions of the image. In +this case, we can still compute the aspect ratio and supplement either +the width based on the height or vice versa. This is accomplished by +calling the |set_image_dimensions| function. It returns true on +success and false otherwise. + +@= +static bool set_image_dimensions(pointer p, double w, double h, bool absolute) +{ if (image_width(p)!=0) + { double aspect=h/w; + image_height(p)=round(image_width(p)*aspect); + } + else if (image_height(p)!=0) + { double aspect=w/h; + image_width(p)=round(image_height(p)*aspect); + } + else + { if (!absolute) return false; + image_width(p)=round(unity*w); + image_height(p)=round(unity*h); + } + return true; +} +@ +We call the following routines with the image buffer partly filled +with the start of the image file. + +@= +#define IMG_BUF_MAX 54 +#define IMG_HEAD_MAX 2 +static unsigned char img_buf[IMG_BUF_MAX]; +static size_t img_buf_size; +#define @[LittleEndian32(X)@] (img_buf[(X)]+(img_buf[(X)+1]<<8)+\ + (img_buf[(X)+2]<<16)+(img_buf[(X)+3]<<24)) + +#define @[BigEndian16(X)@] (img_buf[(X)+1]+(img_buf[(X)]<<8)) + +#define @[BigEndian32(X)@] (img_buf[(X)+3]+(img_buf[(X)+2]<<8)+\ + (img_buf[(X)+1]<<16)+(img_buf[(X)]<<24)) + +#define Match2(X,A,B) ((img_buf[(X)]==(A)) && (img_buf[(X)+1]==(B))) +#define Match4(X,A,B,C,D) (Match2(X,A,B)&&Match2((X)+2,C,D)) + +#define @[GET_IMG_BUF(X)@] \ +if (img_buf_size= +static bool get_BMP_info(FILE *f, char *fn, pointer p) +{ double w,h; + double xppm,yppm; + GET_IMG_BUF(2); + if (!Match2(0,'B','M')) return false; + GET_IMG_BUF(0x2E); + w=(double)LittleEndian32(0x12); /*width in pixel*/ + h=(double)LittleEndian32(0x16); /*height in pixel*/ + xppm=(double)LittleEndian32(0x26); /* horizontal pixel per meter*/ + yppm=(double)LittleEndian32(0x2A); /* vertical pixel per meter*/ + return set_image_dimensions(p,(72.27*1000.0/25.4)*w/xppm,(72.27*1000.0/25.4)*h/yppm,true); +} +@ + +\subsubsection{PNG} +Now we repeat this process for image files in using the Portable Network Graphics\cite{png2nd} file +format. This file format is well suited to simple graphics that do not use color gradients. +These images usually have the extension {\tt .png} and start with an eight byte signature: +|0x89| followed by the ASCII Codes `P', `N', `G', followd by a carriage return (|0x0D| and line feed (|0x0A|), +an DOS end-of-file character (|0x1A|) and final line feed (|0x0A|). +After the signature follows a list of chunks. The first chunk is the image header chunk. +Each chunk starts with the size of the chunk stored as big-endian 32 bit integer, followed by the chunk name +stored as four ASCII codes followed by the chunk data and a CRC. The size as stored in the chunk +does not include the size itself, the name or the CRC. +The first chunk is the IHDR chunk. +The chunk data of the IHDR chunk starts with the width and the height of the image in pixels +stored as 32 bit big-endian integers. + +Finding the image resolution takes some more effort. The image resolution is stored in an optional chunk +named ``pHYs'' for the physical pixel dimensions. +All we know is that this chunk, if it exists, will appear after the IHDR +chunk and before the (required) IDAT chunk. The pHYs chunk contains two 32 bit big-endian integers, +giving the horizontal and vertical pixels per unit, and a one byte unit specifier, which is either 0 +for an undefined unit or 1 for the meter as unit. With an undefined unit only the aspect ratio +of the pixels anh hence the aspect ratio of the image can be determined. + + +@= + +static bool get_PNG_info(FILE *f, char *fn, pointer p) +{ int pos, size; + double w,h; + double xppu,yppu; + int unit; + GET_IMG_BUF(24); + if (!Match4(0, 0x89, 'P', 'N', 'G') || + !Match4(4, 0x0D, 0x0A, 0x1A, 0x0A)) return false; + size=BigEndian32(8); + if (!Match4(12,'I', 'H', 'D', 'R')) return false; + w=(double)BigEndian32(16); + h=(double)BigEndian32(20); + pos=20+size; + while (true) + { if (fseek(f,pos,SEEK_SET)!=0) return false; + img_buf_size=0; + GET_IMG_BUF(17); + size=BigEndian32(0); + if (Match4(4,'p', 'H', 'Y', 's')) + { xppu =(double)BigEndian32(8); + yppu =(double)BigEndian32(12); + unit=img_buf[16]; + if (unit==0) + return set_image_dimensions(p,w/xppu,h/yppu,false); + else if (unit==1) + return set_image_dimensions(p,(72.27/0.0254)*w/xppu,(72.27/0.0254)*h/yppu,true); + else + return false; + } + else if (Match4(4,'I', 'D', 'A', 'T')) + return set_image_dimensions(p,w,h,false); + else + pos=pos+12+size; + } + return false; +} +@ + +\subsubsection{JPG} +For photographs, the JPEG File Interchange Format (JFIF)\cite{Ham92}\cite{JPEG93} is more appropriate. +JPEG files come with all sorts of file extensions like {\tt .jpg}, {\tt .jpeg}, or {\tt .jfif}. +We check the file siganture: it starts with the the SOI (Start of Image) marker |0xFF|, |0xD8| +followed by the JIFI-Tag. The JIFI-Tag starts with the segment marker APP0 (|0xFF|, |0xE0|) followed by the +2 byte segment size, followed by the ASCII codes `J', `F', `I', `F' followed by a zero byte. +Next is a two byte version number which we do not read. +Before the resolution proper there is a resolution unit indicator byte (0 = no units, +1 = dots per inch, 2 = dots per cm) and then comes the horizontal and vertical resolution both +as 16 Bit big-endian integers. +To find the actual width and height, we have to search for a start of frame marker (|0xFF|, |0xC0|+$n$ with $0\le n\le 15$). Which is followed by the 2 byte segment size, the 1 byte sample precission, the +2 byte height and the 2 byte width. + + +@= + +static bool get_JPG_info(FILE *f, char *fn, pointer p) +{ int pos, size; + double w,h; + double xppu,yppu; + int unit; + GET_IMG_BUF(18); + + if (!Match4(0, 0xFF,0xD8, 0xFF, 0xE0)) return false; + size=BigEndian16(4); + if (!Match4(6,'J', 'F', 'I', 'F')) return false; + if (img_buf[10] != 0) return false; + unit=img_buf[13]; + xppu=(double)BigEndian16(14); + yppu=(double)BigEndian16(16); + pos=4+size; + while (true) + { if (fseek(f,pos,SEEK_SET)!=0) return false; + img_buf_size=0; + GET_IMG_BUF(10); + if (img_buf[0] != 0xFF) return false; /* Not the start of a segment */ + if ( (img_buf[1]&0xF0) == 0xC0) /* Start of Frame */ + { h =(double)BigEndian16(5); + w =(double)BigEndian16(7); + if (unit==0) + return set_image_dimensions(p,w/xppu,h/yppu,false); + else if (unit==1) + return set_image_dimensions(p,72.27*w/xppu,72.27*h/yppu,true); + else if (unit==2) + return set_image_dimensions(p,(72.27/2.54)*w/xppu,(72.27/2.54)*h/yppu,true); + else + return false; + } + else + { size= BigEndian16(2); + pos=pos+2+size; + } + } + return false; +} +@ + +\subsubsection{SVG} +There is still one image format missing: scalable vector graphics. +In the moment, I tend not to include a further image format into +the definition of the \HINT\ file format but instead use the +PostScript subset that is used for Type 1 fonts to encode +vector graphics. Any \HINT\ viewer must support Type 1 +PostScript fonts and hence it has already the necessary interpreter. +So it seems reasonable to put the burden of converting vector graphics +into a Type 1 PostScript font on the generator of \HINT\ files +and keep the \HINT\ viewer as small and simple as possible. + + +\subsection{Links, Labels, and Outlines} +The \HINT\ format knows about labels, links, and outlines. +When generating a short format \HINT\ file, links are part of +the content section, where as labels and outlines are found in +the definition section. Because labels are defined while +writing the content section, the writing of labels and outlines, which +reference the labels, must be postponed. For that reason, +we store information about labels and outlines in dynamic arrays, +and map labels, which are identified by a name or a number, +to their index using a dynamic hash table. + +We start with two functions that allocate new entries in the +dynamic arrays increasing their size if necessary. +@= +static int next_label(void) +{ static int label_no=-1; + static int labels_allocated =0; + label_no++; + if (label_no>0xFFFF) + overflow("labels",0xFFFF); + if (label_no>=labels_allocated) + { if (labels_allocated==0) + { labels_allocated=32; ALLOCATE(labels,labels_allocated,label_t); } + else RESIZE(labels,labels_allocated,label_t); + } + max_ref[label_kind]=label_no; + return label_no; +} + +static int next_outline(void) +{ static int outlines_allocated =0; + static int outline_no=-1; + outline_no++; + if (outline_no>0xFFFF) + overflow("outlines",0xFFFF); + if (outline_no>=outlines_allocated) + { if (outlines_allocated==0) + { outlines_allocated=32; ALLOCATE(outlines,outlines_allocated,outline_t); } + else RESIZE(outlines,outlines_allocated,outline_t); + } + max_outline=outline_no; + return outline_no; +} +@ + +While processing the content nodes, access to the labels is provided either +by name or by number through a hash table. We store tabel entries in linked +lists starting with a reasonably sized table of pointers. This keeps +the fixed costs low and guards against overflow and rapidly increasing +inefficiency. We start with a function to insert a new entry into +the hash table. + +@= +typedef struct hash_entry +{int num; char *nom; uint16_t n; struct hash_entry *next;} hash_entry_t; +#define LABEL_HASH 1009 /* MIX a prime number */ +static hash_entry_t *label_hash[LABEL_HASH]={NULL}; + +static int insert_hash(int h, int num, char *nom) +{ hash_entry_t *e; + ALLOCATE(e,1,hash_entry_t); + e->n= next_label(); + if (nom!=NULL) e->nom=strdup(nom); + else e->num=num; + e->next= label_hash[h]; + label_hash[h]=e; + if (e->nom!=NULL) + DBG(DBGLABEL,"Creating new label *%d: name='%s'\n",e->n,e->nom); + else + DBG(DBGLABEL,"Creating new label *%d: num=%d\n",e->n,e->num); + return e->n; +} +@ + +There are two cases: finding a label by name or by number. +We start with the simpler case where the number is given. +The process is straigth forward: + +@= +static int find_label_by_number(int p) +{ unsigned int h=(unsigned int)p%LABEL_HASH; + hash_entry_t *e= label_hash[h]; + while (e!=NULL) + if (e->nom==NULL && e->num==p) return e->n; + else e=e->next; + return insert_hash(h,p,NULL); +} +@ + +To look up a label by its name as given by a token list, +we prepare ourselfs by implementing two functions: +one to extract the character codes from the token list +forming the ``name'' +and one to compute the hash value for a name. +The routine to find the label by name is then equivalent to the +routine we have just seen. Given a pointer |p| to +either a label, a link, or an outline node, the last function +returns the correct label reference. +Currently, we limit label names to at most 255 byte not counting the +zero byte. + +@= +static char *tokens_to_name(pointer p) +{ static char s[256]; + int i=0; + bool skip_space=0; + while (i<255 && p!=0)@/ + { int m = info(p)/0400;@+ + int c = info(p)%0400; + if (m==spacer && ! skip_space) @/ + { s[i++]=' '; skip_space=true;@+} + else if ((m==letter || m==other_char) && ' '< c && c < 0x7F)@/ + {@+ s[i++]=c; skip_space=false;@+} + p=link(p); + } + s[i]=0; + return s; +} + +static unsigned int name_hash(char *s) +{ unsigned int h=0; + while (*s!=0) + h=(h<<2)+*(s++); + return h; +} + +static int find_label_by_name(pointer p) +{ char *s=tokens_to_name(link(p)); + unsigned int h=name_hash(s)%LABEL_HASH; + hash_entry_t *e= label_hash[h]; + while (e!=NULL) + if (e->nom!=NULL && strcmp(e->nom,s)==0) return e->n; + else e=e->next; + return insert_hash(h,0,s); +} +@ + +We combine both ways of finding a label reference in the following function: + +@= +static int find_label(pointer p) +{@+ if (label_has_name(p)) return find_label_by_name(label_ptr(p)); + else return find_label_by_number(label_ptr(p)); +} +@ + +After these preparations, we can implement the functions needed +when labels, links, and outlines are delivered to the page builder. + +We start with looking at the labels: +When a labels is defined, the current position is recorded. +Further labels are linked together in order of descending positions, +to allow the efficient adjustment of label positions when +moving lists. + +@= +static void new_label(pointer p) +{ int n=find_label(p); + if (n!=zero_label_no && labels[n].where!=LABEL_UNDEF) + { MESSAGE("WARNING: Ignoring duplicate definition of label "); + if (label_has_name(p)) MESSAGE("name %s\n",tokens_to_name(link(label_ptr(p)))); + else MESSAGE("num %d\n",label_ptr(p)); + } + else + { labels[n].where=label_where(p); + labels[n].pos=hpos-hstart; + labels[n].pos0=hpos0-hstart; + labels[n].next=first_label; + first_label=n; + DBG(DBGLABEL,"Defining label *%d: pos=0x%x\n",n,labels[n].pos); + } +} +@ + +When a link node is written to the output, we can check +that start links and end links properly match. + +@= +static int last_link=-1; +static int new_start_link(pointer p) +{ int n=find_label(p); + if (last_link>=0) + fatal_error("Missing end link before start link"); + labels[n].used=true; + last_link=n; + DBG(DBGLABEL,"New link to label *%d\n",n); + return n; +} + +static int new_end_link(void) +{ int n; + if (last_link<0) + fatal_error("Missing start link before end link"); + n=last_link; + last_link=-1; + return n; +} +@ + +For outline nodes, we use the next two functions. +The node list representing the title can be an arbitrary +list in horizontal mode. +In general, the front end should be able to render such a +horizontal list, but at least it should be able to extract +the UTF8 characters codes and display those. + +@= +static void new_outline(pointer p) +{ int r=find_label(p); + int m=next_outline(); + list_t l; + uint32_t pos; + pos=hpos-hstart; + l.k=list_kind; /* this eventually should be |text_kind| */ + hout_list_node(outline_ptr(p),pos,&l); + hset_outline(m,r,outline_depth(p),pos); + DBG(DBGLABEL,"New outline for label *%d\n",r); +} +@ + +One last function is needed, which better schould be in the {\tt htex.w} +file. It is called when the |outline_group| ends that was started +when scanning the {\tt\BS HINToutline} primitive. +@= +void hfinish_outline_group(void) +{ pointer s=link(head); + unsave(); + pop_nest(); + outline_ptr(tail)=s; +} +@ + +\subsection{The New Page Builder}\label{buildpage} +Here is the new |build_page| routine of Hi\TeX: + +@= +void build_page(void) +{ static bool initial=true; + if(link(contrib_head)==null||output_active)return; + do + { pointer p= link(contrib_head); + pointer q=null; /* for output nodes */ + pointer *t; /*the tail of the output nodes*/ + @@; + @@; + link(contrib_head)= link(p);link(p)= null; + if (link(contrib_head)==null) + { if(nest_ptr==0) tail= contrib_head; + else contrib_tail= contrib_head; + } + update_last_values(p); + @@; + page_goal=0x3fffffff; /* maximum dimension */ + t=collect_output(&p,&q); + if (p!=null) + { hpos0=hpos; hout_node(p); } +recycle_p: + flush_node_list(p); + if (q!=null) + @@; + } while(link(contrib_head)!=null); + DBG(DBGBUFFER,"after build page dyn_used= %d\n", dyn_used); +} +@ + +@= +if (page_contents= +if (option_no_empty_page && + ((type(p)==penalty_node && + penalty(p)<=eject_penalty && penalty(p)>2*(eject_penalty)) || + (page_contents==empty && !is_visible(p)))) +{ pointer q= link(p); + while (true) + { if (q==null) return; + else if (is_visible(q)) break; + else if (type(q)==penalty_node && penalty(q)<=eject_penalty) + { while (p!=q) + { pointer r=p; + DBG(DBGPAGE,"Eliminating node (%d,%d)\n", + type(p), type(p)==penalty_node?penalty(p):subtype(p)); + p=link(p); + link(r)=null; + flush_node_list(r); + } + link(contrib_head)= p; + DBG(DBGPAGE,"Eliminating empty page done\n"); + if (penalty(q)<=2*(eject_penalty)) break; + } + q=link(q); + } +} +@ +It remains to test a node for visibility. This is a quick (and dirty) test +because the test will not look inside boxes; it simply tests whether +the list pointer is |null|. We consider an |open_node|, |write_node|, +|close_node|, |label_node|, or |outline_node| a as visible, +because deleting them could cause unwanted +side effects. Possibly it would be better to regard them as invisible, +but still pass them on to the rest of the output routine. + +@= +static bool is_visible(pointer p) +{ switch (type(p)) + { case penalty_node: + case kern_node: + case glue_node: + case mark_node: + return false; + case ins_node: + return ins_ptr(p)!=null; + case adjust_node: + return adjust_ptr(p)!=null; + case hlist_node: + case vlist_node: + return list_ptr(p)!=null; + case whatsit_node: + if (subtype(p)==image_node || subtype(p)==align_node || subtype(p)==disp_node || + subtype(p)==open_node ||subtype(p)==write_node ||subtype(p)==close_node || + subtype(p)==label_node || subtype(p)==outline_node ) + return true; + else if (subtype(p)==hset_node || subtype(p)==vset_node || + subtype(p)==hpack_node || subtype(p)==vpack_node) + return list_ptr(p)!=null; + else if (subtype(p)==graf_node) + return graf_list(p)!=null; + else + return false; + default: return true; + } +} +@ + +An important feature of the new routine is the call to +|hfix_defaults|. It occurs when the first ``visible mark'' is placed +on the page. At that point we record the current values of \TeX's +parameters which we will use to generate the definition section of the +\HINT\ file. It is still possible to specify alternative values for +these parameters by using parameter lists but only at an additional +cost in space and time. + +Furthermore, this is the point where we freeze the definition of +|hsize| and |vsize|. The current values will be regarded as the sizes +as recommended by the author. + +From then on |hsize| and |vsize| are replaced by the equivalent +extended dimensions and any attempt to modify them on the global level +will be ignored. |hhsize| and |hvsize| will contain the sizes that a +regular \TeX\ engine would use. + +We also compute the total page size from the page template defined +last. + + +@= +{ pointer p; + p=link(setpage_head); + if (p==null) + { scaled margin; + if (hhsize= +static scaled page_h, page_v; +@ + +@= + hsize=0; vsize=0; + dimen_par_hfactor(hsize_code)= unity; + dimen_par_vfactor(vsize_code)= unity; +@ + +There is one point where we can not simpy forego the +output routine: \.{\\write} commands. Unless the \.{\\write} is +decorated with an \.{\\immediate}, the whatsit node generated from it +will lay dormant in the contribution list and then the page until +the output routine passes it inside the finished page to the |ship_out| +routine where it will come to life and write its token list out. +The whatsit nodes from \.{\\openout} and \.{\\closeout} commands +behave similary. + +It is not possible to ignore the output routine +because the output routine may change the environment in which the +token list of a \.{\\write} will be expanded. +For example \LaTeX\ redefines \.{\\protect} to be \.{\\noexpand}. +As a consequence we have to implement a simplified version +of \TeX's usual process to fire up the output routine. + +The |collect_output| routine takes a node list |p|, +removes the output nodes and appends them to |q|, with |q| +always pointing to the tail pointer. + +@= +static pointer *collect_output(pointer *p, pointer *q) +{ while (*p!=null) + { @@; + p=&(link(*p)); + } + return q; +} +@ + +\TeX\ does not permit output nodes in leaders, so we dont check them; +further we do not check the pre- and post-break lists of +discretionary breaks. + +@= +if (!is_char_node(*p)) +{ pointer r=*p; + switch (type(r)) + { case whatsit_node: + switch (subtype(r)) + { case open_node: case write_node: case close_node: + { *p=link(r); link(r)=null; *q=r; q=&(link(r)); + if (*p==null) return q; + } + break; + case graf_node: q=collect_output(&graf_list(r),q); + break; + case disp_node: + if (display_left(r)) q=collect_output(&display_eqno(r),q); + q=collect_output(&display_formula(r),q); + if (!display_left(r)) q=collect_output(&display_eqno(r),q); + break; + case hset_node: case vset_node: case hpack_node: case vpack_node: + q=collect_output(&list_ptr(r),q); + break; + case align_node: + q=collect_output(&align_list(r),q); + break; + default: break; + } + break; + case hlist_node: case vlist_node: + q=collect_output(&list_ptr(r),q); + break; + case ins_node: + q=collect_output(&ins_ptr(r),q); + break; + case adjust_node: + q=collect_output(&adjust_ptr(r),q); + break; + default: break; + } +} +@ + + + +@= +{ pointer r=new_null_box();type(r)=vlist_node; + subtype(r)=0;shift_amount(r)=0;height(r)=hvsize; + list_ptr(r)=q; + *t=new_glue(ss_glue); + flush_node_list(box(255)); /* just in case \dots */ + box(255)=r; + if (output_routine!=null) + {@+output_active=true; + if (bot_mark!=null) + {@+if (top_mark!=null) delete_token_ref(top_mark); + top_mark=bot_mark;add_token_ref(top_mark); + if (first_mark!=null) delete_token_ref(first_mark); + first_mark=bot_mark;add_token_ref(first_mark); + } + 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; + } + else + { + ship_out(box(255)); box(255)=null; + } +} +@ + +The |ship_out| routine just calls |execute_output|. +Because the output routine might have added plenty +of decorations around the list of output nodes, +we have to find them again. + +@= +void execute_output(pointer p) +{@+while (p!=null) + { @@; + p=link(p); + } +} +@ + +@= +if (!is_char_node(p)) + switch (type(p)) + { case whatsit_node: + switch (subtype(p)) + { case open_node: case write_node: case close_node: + out_what(p); + break; + case graf_node: execute_output(graf_list(p)); + break; + case disp_node: + if (display_left(p)) execute_output(display_eqno(p)); + execute_output(display_formula(p)); + if (!display_left(p)) execute_output(display_eqno(p)); + break; + case hset_node: case vset_node: case hpack_node: case vpack_node: + execute_output(list_ptr(p)); + break; + case align_node: + execute_output(align_list(p)); + break; + default: break; + } + break; + case hlist_node: case vlist_node: + execute_output(list_ptr(p)); + break; + case ins_node: + execute_output(ins_ptr(p)); + break; + case adjust_node: + execute_output(adjust_ptr(p)); + break; + default: break; + } +@ + +Invoking the user's output routine is a risky endeavor +if marks are not initialized properly. In our case +we will have always |top_mark| equal to |first_mark| and +|bot_mark|. + +@= +if (type(p)==mark_node) +{ if (bot_mark!=null) delete_token_ref(bot_mark); + bot_mark=mark_ptr(p);add_token_ref(bot_mark); +} +@ + + +\subsection{Replacing {\tt hpack} and {\tt vpack}} + + +@= + +pointer hpack(pointer p,scaled w, scaled hf, scaled vf, small_number m) +{ + pointer r; /*the box node that will be returned*/ + pointer prev_p; /*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 sto, sho; /*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; + prev_p= r+list_offset;link(prev_p)= p; + h= 0; 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; + while(p!=null) + { + reswitch: + while(is_char_node(p)) + { 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); + } + if (p!=null) + { switch(type(p)){ + case hlist_node:case vlist_node:case rule_node:case unset_node: + { 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; + } + break; + case ins_node:case mark_node:case adjust_node:if (adjust_tail!=null) + { while(link(prev_p)!=p)prev_p= link(prev_p); + 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(prev_p),small_node_size); + } + else + { link(adjust_tail)= p;adjust_tail= p;p= link(p); + } + link(prev_p)= p;p= prev_p; + } + break; + case whatsit_node: + if (subtype(p)==graf_node) + goto repack; + else if (subtype(p)==disp_node ) + goto repack; + else if (subtype(p)==vpack_node ) + goto repack; + else if (subtype(p)==hpack_node ) + goto repack; + else if (subtype(p)==hset_node ) + goto repack; + else if (subtype(p)==vset_node ) + goto repack; + else if (subtype(p)==stream_node ) + goto repack; + else if (subtype(p)==image_node) + { glue_ord o; + if (image_height(p)> h) h= image_height(p); + x= x+image_width(p); + o= image_stretch_order(p);total_stretch[o]= total_stretch[o]+image_stretch(p); + o= image_shrink_order(p);total_shrink[o]= total_shrink[o]+image_shrink(p); + } + break; + break; + case glue_node: + { glue_ord o; + 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); + } + } + break; + case kern_node:case math_node:x= x+width(p);break; + case ligature_node: + { mem[lig_trick]= mem[lig_char(p)];link(lig_trick)= link(p); + p= lig_trick;goto reswitch; + } + default:do_nothing; + } + p= link(p); + } + } + if (adjust_tail!=null) link(adjust_tail)= null; + height(r)= h;depth(r)= d; + if (total_stretch[filll]!=0)sto= filll; + else if (total_stretch[fill]!=0)sto= fill; + else if (total_stretch[fil]!=0)sto= fil; + else sto= normal; + + if (total_shrink[filll]!=0)sho= filll; + else if (total_shrink[fill]!=0)sho= fill; + else if (total_shrink[fil]!=0)sho= fil; + else sho= normal; + + if (hf!=0 || vf!=0 ) /* convert to a hset node */ + { pointer q; + q=new_set_node(); + subtype(q)=hset_node; + height(q)=h; + depth(q)=d; + width(q)=x; /* the natural width */ + shift_amount(q)=shift_amount(r); + list_ptr(q)=list_ptr(r); + list_ptr(r)=null; + free_node(r, box_node_size); + if (m==exactly) + set_extent(q)=new_xdimen(w,hf,vf); + else + set_extent(q)=new_xdimen(x+w,hf,vf); + set_stretch_order(q)=sto; + set_shrink_order(q)=sho; + set_stretch(q)=total_stretch[sto]; + set_shrink(q)=total_shrink[sho]; + return q; + } + + + + + 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) + { + glue_order(r)= sto;glue_sign(r)= stretching; + if (total_stretch[sto]!=0)glue_set(r)= unfloat(x/(double)total_stretch[sto]); + else + { glue_sign(r)= normal; + set_glue_ratio_zero(glue_set(r)); + } + if (sto==normal) + { if (list_ptr(r)!=null) + { 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); + goto common_ending; + } + } + } + goto end; + } + else + { + glue_order(r)= sho;glue_sign(r)= shrinking; + if (total_shrink[sho]!=0) + glue_set(r)= unfloat((-x)/(double)total_shrink[sho]); + else + { glue_sign(r)= normal; + set_glue_ratio_zero(glue_set(r)); + } + if ((total_shrink[sho]<-x)&&(sho==normal)&&(list_ptr(r)!=null)) + { last_badness= 1000000; + set_glue_ratio_one(glue_set(r)); + if ((-x-total_shrink[normal]> hfuzz)||(hbadness<100)) + { if ((overfull_rule> 0)&&(-x-total_shrink[normal]> hfuzz)) + { while(link(prev_p)!=null)prev_p= link(prev_p); + link(prev_p)= new_rule(); + width(link(prev_p))= overfull_rule; + } + print_ln();print_nl("Overfull \\hbox ("); + print_scaled(-x-total_shrink[normal]);print("pt too wide"); + goto common_ending; + } + } + else if (sho==normal) + { if (list_ptr(r)!=null) + { last_badness= badness(-x,total_shrink[normal]); + if (last_badness> hbadness) + { print_ln();print_nl("Tight \\hbox (badness ");print_int(last_badness); + goto common_ending; + } + } + } + goto end; + } + common_ending: + 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); + end:return r; + + +repack: + { /* convert the box to a |hpack_node| */ + pointer q; + q=new_pack_node(); + height(q)=h; + depth(q)=d; + width(q)=x; + subtype(q)=hpack_node; + list_ptr(q)=list_ptr(r); + list_ptr(r)=null; + free_node(r, box_node_size); + pack_limit(q)=max_dimen; /* no limit, not used */ + pack_m(q)=m; + pack_extent(q)=new_xdimen(w,hf,vf); + return q; + } +} + +@ + +@= +pointer vpackage(pointer p, scaled h, scaled hf, scaled vf, 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=0; /*shift amount*/ + pointer g; /*points to a glue specification*/ + glue_ord sho, sto; /*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; + 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; + while(p!=null) + { if (is_char_node(p)) + confusion("vpack"); + else + switch(type(p)) + { case hlist_node:case vlist_node:case rule_node:case unset_node: + 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; + break; + case whatsit_node: + if (subtype(p)==graf_node) + goto repack; + else if (subtype(p)==disp_node ) + goto repack; + else if (subtype(p)==vpack_node ) + goto repack; + else if (subtype(p)==hpack_node ) + goto repack; + else if (subtype(p)==hset_node ) + goto repack; + else if (subtype(p)==vset_node ) + goto repack; + else if (subtype(p)==stream_node ) + goto repack; + else if (subtype(p)==image_node) + { glue_ord o; + if (image_width(p)> w) w= image_width(p); + x= x+d+image_height(p);d=0; + o= image_stretch_order(p);total_stretch[o]= total_stretch[o]+image_stretch(p); + o= image_shrink_order(p);total_shrink[o]= total_shrink[o]+image_shrink(p); + } + break; + case glue_node: + { glue_ord o; + 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); + } + } + break; + case kern_node: + x= x+d+width(p);d= 0; + break; + default:do_nothing; + } + p= link(p); + } + width(r)= w; + + + if (total_stretch[filll]!=0) sto= filll; + else if (total_stretch[fill]!=0) sto= fill; + else if (total_stretch[fil]!=0) sto= fil; + else sto= normal; + + if (total_shrink[filll]!=0) sho= filll; + else if (total_shrink[fill]!=0) sho= fill; + else if (total_shrink[fil]!=0) sho= fil; + else sho= normal; + + if (hf!=0 || vf!=0) /* convert to a vset node */ + { pointer q; + q=new_set_node(); + subtype(q)=vset_node; + width(q)=w; + if (d> l) + { x= x+d-l;depth(r)= l; + } + else depth(r)= d; + height(q)=x; + depth(q)=d; + shift_amount(q)=shift_amount(r); + list_ptr(q)=list_ptr(r); + list_ptr(r)=null; + free_node(r, box_node_size); + if (m==exactly) + set_extent(q)=new_xdimen(h,hf,vf); + else + set_extent(q)=new_xdimen(x+h,hf,vf); + set_stretch_order(q)=sto; + set_shrink_order(q)=sho; + set_stretch(q)=total_stretch[sto]; + set_shrink(q)=total_shrink[sho]; + return q; + } + + if (d> l) + { x= x+d-l;depth(r)= l; + } + else depth(r)= d; + 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) + { glue_order(r)= sto;glue_sign(r)= stretching; + if (total_stretch[sto]!=0)glue_set(r)= unfloat(x/(double)total_stretch[sto]); + else + { glue_sign(r)= normal; + set_glue_ratio_zero(glue_set(r)); + } + if (sto==normal) + { if (list_ptr(r)!=null) + { 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); + goto common_ending; + } + } + } + goto end; + } + else /* if (x<0) */ + { + glue_order(r)= sho;glue_sign(r)= shrinking; + if (total_shrink[sho]!=0)glue_set(r)= unfloat((-x)/(double)total_shrink[sho]); + else + { glue_sign(r)= normal; + set_glue_ratio_zero(glue_set(r)); + } + if ((total_shrink[sho]<-x)&&(sho==normal)&&(list_ptr(r)!=null)) + { last_badness= 1000000; + set_glue_ratio_one(glue_set(r)); + if ((-x-total_shrink[normal]> vfuzz)||(vbadness<100)) + { print_ln();print_nl("Overfull \\vbox ("); + print_scaled(-x-total_shrink[normal]);print("pt too high"); + goto common_ending; + } + } + else if (sho==normal) + { if (list_ptr(r)!=null) + { last_badness= badness(-x,total_shrink[normal]); + if (last_badness> vbadness) + { print_ln();print_nl("Tight \\vbox (badness ");print_int(last_badness); + goto common_ending; + } + } + } + goto end; + } + + + common_ending: + if (pack_begin_line!=0) + { 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); + end: + return r; + + +repack: + { /* convert the box to a |vpack_node| */ + pointer q; + q=new_pack_node(); + subtype(q)=vpack_node; + height(q)=x; + depth(q)=d; + width(q)=w; + list_ptr(q)=list_ptr(r); + list_ptr(r)=null; + free_node(r, box_node_size); + pack_limit(q)=l; + pack_m(q)=m; + pack_extent(q)=new_xdimen(h,hf,vf); + return q; + } +} + +@ + +\subsection{Streams} +\HINT\ stream numbers start at 0 for the main text and continue +upwards. \TeX, on the other hand, numbers insertions starting with +{\tt box255} for the main text and continues downwards. Some mapping is +needed, and we use the array |insert2stream| to map \TeX's insert +numbers to \HINT\ stream numbers. +The predefined stream for the main content has stream number 0. + +@= +int insert2stream[0x100]={0}; +@ + +The following function returns the stream number for a given insert number $i$ +with $255>|i|\ge 0$. A new stream number is allocated if necessary. +Note that no overflow test is necessary since \TeX\ allocates less +than 233 inserts. +The initial value of |max_ref[stream_kind]| is 0 and therefore +stream number 0, reserved for the main content +is never allocated. Stream definitions might also be loaded +as part of a format file. Then the maximum stream number ist stored in |max_stream|. +So if we do not find a stream number +in the |insert2stream| array, we scan the stream definitions +once and cache the associations found there. + +@= +int hget_stream_no(int i) +{ static bool init=false; + int s; + if (i==0) return 0; + s=insert2stream[i]; + if (s!=0) return s; + if (!init) + { pointer t,s; + for (t=link(setpage_head); t!=null; t=link(t)) + for(s=setpage_streams(t); s!=null; s=link(s)) + insert2stream[setstream_insertion(s)]=setstream_number(s); + max_ref[stream_kind]=max_stream; + init=true; + } + s=insert2stream[i]; + if (s==0) + s=insert2stream[i]=max_ref[stream_kind]=++max_stream; + return s; +} +@ + +\subsection{Stream Definitions} + +A stream definition is stored as a whatsit node with subtype |setstream_node| as +defined in section~\secref{whatsit}. +Given a pointer |p| to such a node, here are the macros used to access the data stored there: +\itemize +\item |setstream_number(p)| the \HINT\ stream number $n$. +\item |setstream_insertion(p)| the corresponding \TeX\ insertion number $i$. +\item |setstream_max(p)| the maximum height $x$: This extended dimension is the maximum size + per page for this insertion. +\item |setstream_mag(p)| the magnification factor $f$: + Inserting a box of height $h$ will contribute $h*f/1000$ + to the main page. +\item |setstream_prefered(p)| the prefered stream $p$: + If $p\ge0$ we move the insert to stream $p$ if possible. +\item |setstream_next(p)| the next stream $n$: + If $n\ge0$ we move the insert to stream $n$ if it can not be + accomodated otherwise. +\item |setstream_ratio(p)| the split ratio $r$: + If $r>0$ split the final contribution of this streams between + stream $p$ and $n$ in the ratio $r/1000$ for $p$ and $1-r/1000$ for $n$ + before contributing streams $p$ and $r$ to the page. +\item |setstream_before(p)| the ``before'' list $b$: + For a nonempty stream the material that is added before the stream content. +\item |setstream_after(p)| the ``after'' list $a$: + For a nonempty stream, material that is added after the stream conten. +\item |setstream_topskip(p)| the top skip glue $t$: This glue is inserted between + the $b$ list and the stream content and ajusted for the height for the first box + of the stream content. +\item |setstream_width(p)| the width $w$: + This extended dimension is the width used for example + to break paragraphs in the stream content into lines. +\item |setstream_height(p)| a glue specification $h$ reflecting the total height, + strechability and shrinkability of the material in lists $a$ and $b$. +\enditemize + +Currently Hi\TeX\ handles only normal streams. First or last streams will come later. + +The stream definition nodes are created and initialized with the following function: +@= +pointer new_setstream_node(uint8_t n) +{ pointer p=get_node(setstream_node_size); + type(p)=whatsit_node;subtype(p)=setstream_node; + setstream_insertion(p)=n; + setstream_number(p)=hget_stream_no(n); + setstream_mag(p)=1000; + setstream_prefered(p)=255; + setstream_next(p)=255; + setstream_ratio(p)=0; + setstream_max(p)=new_xdimen(0,0,ONE); + setstream_width(p)=new_xdimen(0,ONE,0); + setstream_topskip(p)=zero_glue; add_glue_ref(zero_glue); + setstream_height(p)=zero_glue; add_glue_ref(zero_glue); + setstream_before(p)=null; + setstream_after(p)=null; + + return p; +} +@ + + +The prefered stream, the next stream, and the split ratio are scanned as part of the +{\tt \BS setstream} primitive as described in section~\secref{primitive}. +When \TeX\ finds the right brace that terminates the stream definition, +it calls |handle_right_brace|. Then it is time to obtain the remaining parts of the +stream definition. +For insertion class $i$, +we can extract the maximum height $x$ of the insertions from +the corresponding {\tt dimen$i$} register +the magnification factor $f$ from the {\tt count$i$} register, +and the total height $h$ from the {\tt skip$i$} register. +The width $w$ is taken from {\tt \BS hsize} and the +topskip $t$ from {\tt \BS topskip}. + +@= +void hfinish_stream_group(void) +{ pointer s; + end_graf(); + s=hget_current_stream(); + if (s!=null) + { pointer t; + uint8_t i; + i= setstream_insertion(s); + setstream_mag(s)=count(i); + setstream_width(s)=new_xdimen(dimen_par(hsize_code), + dimen_par_hfactor(hsize_code),dimen_par_vfactor(hsize_code)); + t=zero_glue;add_glue_ref(t);delete_glue_ref(setstream_topskip(s)); setstream_topskip(s)=t; + t=skip(i);add_glue_ref(t);delete_glue_ref(setstream_height(s)); setstream_height(s)=t; + setstream_max(s)=new_xdimen(dimen(i), + dimen_hfactor(i),dimen_vfactor(i)); + } + unsave(); + flush_node_list(link(head)); + pop_nest(); +} +@ + +The before list $b$ and the after list $a$ are defined using the +{\tt \BS before} and {\tt \BS after} primitives. When the corresponding list +has ended with a right brace, \TeX\ calls |handle_right_brace| and we can store +the lists. + +@= +void hfinish_stream_before_group(void) +{ pointer s; + end_graf(); + s=hget_current_stream(); + if (s!=null) + setstream_before(s)=link(head); + unsave(); + pop_nest(); +} + +void hfinish_stream_after_group(void) +{ pointer s; + end_graf(); + s=hget_current_stream(); + if (s!=null) + setstream_after(s)=link(head); + unsave(); + pop_nest(); +} +@ + +\subsection{Page Template Definitions} + +The data describing a page template is stored in a whatsit node with subtype +|setpage_node| as defined insection~\secref{whatsit}. +Given a pointer |p| to such a node, here are the macros used to access the data stored there: +\itemize +\item |setpage_name(p)|: The name of the page template + can be used in the user interface of a \HINT\ viewer. +\item |setpage_number(p)|: The number of the page template that is used in the \HINT\ + file to reference this page template. +\item |setpage_id(p)|: The number of the page template that is used in \TeX\ + to reference this page template. +\item |setpage_priority(p)|: The priority helps in selecting a page template. +\item |setpage_topskip(p)|: The topskip glue is added at the top of a page and + adjusted by the height of the first bos on the page. +\item |setpage_height(p)|: The height of the full page including the margins. +\item |setpage_width(p)|: The width of the full page including the margins. +\item |setpage_depth(p)|: The maximum depth of the page content. If the last box is deeper +than this maximum, the difference is subtracted from the height of the page body. +\item |setpage_list(p)|: The list that defines the page template. After the page builder +has completed a page this list is scanned and page body and nonempty streams +are added at the corresponding insertion points. +\item |setpage_streams(p)|: The list of stream definitions that +belong to this page template. +\enditemize + +To allow \TeX\ to use arbitrary numbers between 1 and 255 for the page templates +while in \HINT\ the numbers of page templates are best consecutive +from 1 to |max_ref[page_kind]==max_page|, we let \TeX\ assign an id and generate +the template number. Because templates might be in format files, the variable +|max_page| will hold the true number. + +The function |new_setpage_node| is called with the page template id +$0<|i|<256$ and a string number for the name |n|. +It allocates and initializes a node if necessary and moves it to the front of the +list of templates. + +@= +pointer new_setpage_node(uint8_t i, str_number n) +{ pointer p, prev_p; + prev_p=setpage_head; + for (p=link(prev_p); p!=null; prev_p=p,p=link(p)) + if (setpage_id(p)==i) break; + if (p==null) + @@; + else + link(prev_p)=link(p); + link(p)=link(setpage_head); + link(setpage_head)=p; + return 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; + setpage_name(p)=n; + setpage_priority(p)=1; + setpage_topskip(p)=zero_glue; add_glue_ref(zero_glue); + setpage_height(p)=new_xdimen(0,0,ONE); + setpage_width(p)=new_xdimen(0,ONE,0); + setpage_depth(p)=max_depth; + setpage_list(p)=null; + setpage_streams(p)=null; +} +@ + +The default values are replaced by parameters given to the {\tt\BS setpage} +primitive (see section~\secref{pageext}) and by the current values of certain +\TeX\ registers when finishing the page template. +page template itself, + +@= +void hfinish_page_group(void) +{ uint8_t k; + pointer p,q,r; + end_graf(); + p=hget_current_page(); + if (p!=null) + { delete_glue_ref(setpage_topskip(p)); + setpage_topskip(p)=top_skip;add_glue_ref(top_skip); + setpage_depth(p)=max_depth; + flush_node_list(setpage_list(p)); + setpage_list(p)=link(head); + } + unsave(); + pop_nest(); +} +@ +@= + +static pointer hget_current_page(void) +{ pointer p=link(setpage_head); + if (p==null) + print_err("end of output group without setpage node"); + return p; +} + +static pointer hget_current_stream(void) +{ pointer p,s; + p=hget_current_page(); + if (p==null) return null; + s=setpage_streams(p); + if (s==null) + print_err("end of setstream group without setstream node"); + return s; +} +@ + + + +\section{\HINT\ Output} + +\subsection{Initialization} +\noindent + +@= +static void hout_init(void) +{ + new_directory(dir_entries); + new_output_buffers(); + max_section_no=2; + hdef_init(); + hput_content_start(); + @@; +} + +extern int option_global; +void hint_open(void) +{ if (job_name==0) open_log_file(); + pack_job_name(".hnt"); + while (!(hout=open_out((char *)name_of_file+1,"wb"))) + prompt_file_name("file name for output",".hnt"); + hlog=stderr; + option_global=true; + hout_init(); +} +@ + +\subsection{Termination} + +@= +#define HITEX_VERSION "1.1" +static void hput_definitions(); +static void hout_terminate(void) +{ hput_content_end(); + hput_definitions(); + hput_directory(); + hput_hint("created by HiTeX Version " HITEX_VERSION); +} + +void hint_close(void) +{ hout_terminate(); + if (hout!=NULL) + fclose(hout); + hout=NULL; +} +@ + + +\subsection{\HINT\ Directory} +There is not much to do here: some code to find a new or existing directory entry, +a variable to hold the number of directory entries allocated, +a function to allocate a new file section, and an auxiliar function to +convert \TeX's file names to ordinary \CEE/ strings. + +@= +for (i=3; i<= max_section_no;i++) + if (dir[i].file_name!=NULL && strcmp(dir[i].file_name,file_name)==0) + return i; +@ + +@= + i = max_section_no; + i++; + if (i>0xFFFF) QUIT("Too many file sections"); + if (i>=dir_entries) + RESIZE(dir,dir_entries,entry_t); + max_section_no=i; + if (max_section_no>0xFFFF) QUIT("Too many sections"); + dir[i].section_no=i; +@ + +@= +#define @[RESIZE(P,S,T)@] \ +{ int _n = (S)*1.4142136 +0.5; \ + if (_n<32) _n=32; \ + { REALLOCATE(P,_n,T); \ + memset((P)+(S),0,(_n-(S))*sizeof(T)); \ + (S)=_n; \ + } \ +} +@ + +@= +static int dir_entries=4; +@ + +@= +static uint16_t hnew_file_section(char *file_name) +{ uint16_t i; + @@; + @@; + dir[i].file_name=strdup(file_name); + return i; +} +@ + +The following function uses \TeX's function |pack_file_name| +to create a new filename from a name |n|, a direcory or ``area'' |a|, +and an extension |e|. \TeX\ will truncate the new filename +to |file_name_size| characters without warning. The new function +will take a |name_length| equal to | file_name_size| as an +indication that truncation has taken place and terminates the +program. The return value converts a \Pascal\ array, starting with index 1, +into a \CEE/ array starting with index 0. + +@= +static char *hfile_name(str_number n, char *a, char *e) +{ pack_file_name(n,a,e); + if (name_length>=file_name_size) + QUIT("File name too long %d >= %d",name_length,file_name_size); + return (char *)name_of_file+1; +} + +@ + +\section{\HINT\ Definitions}\label{definitions} +Definitions are used for two reasons: they provide default values for the parameters +that drive \TeX's algorithms running in the \HINT\ viewer, and they provide a compact notation +for \HINT\ content nodes. + +To find the optimal coding for a \HINT\ file, a global knowledge of the \HINT\ file is necessary. +This would require a two pass process: +in the first pass Hi\TeX\ could gather statistics on the use of parameter values and content +nodes as a basis for making definitions and in the second pass it could encode the content using +these definitions. I consider it, however, more reasonable to write such a two pass optimizer +as a separate program which can be used on any \HINT\ file. +Hence Hi\TeX\ uses a much simpler one pass approach: +\itemize + +\item Hi\TeX\ generates definitions for \TeX-parameters using the + values they have when the first non discardable item appears in + |build_page|. This is usually the case after initial style files have + been processed and we can expect that they set useful default values. + +The procedure that generates these definitions is called |hfix_defaults|: + +@= + +void hfix_defaults(void) +{ @+int i; + DBG(DBGDEF,"Freezing HINT file defaults\n"); + @@; + @@; + @@; + @@; + @@; +} + +@ + +\item Hi\TeX\ generates definitions to be used in content nodes on the fly: +Whenever a routine outputs an item for which a definition might be available, +it calls a {\it hget\_\dots\_no} function. This function returns, if possible, +the reference number of a suitable definition. +If no definition is available, the function will try to allocate a new one, +only if all reference numbers from 0 to |0xFF| are already in use, a $-1$ is +returned to indicate failure. + +There are two possible problems with this approach: We might miss a +very common item because it occurs for the first time late in the +input when all reference numbers are already in use. For example an +extensive index might repeat a certain pattern for each entry. And second, we +might make a definition for an item that occurs only once. Taken +together the definition plus the reference to it requires more space +than the same item without a definition. + +We can hope that the first effect does not occur too often, especially if the +\TeX\ file is short, and we know that the second effect is limited by the +total number of definitions we can make plus four byte of overhead per instance. +\enditemize + +Here we initialize the necessary data structures for definitions. +@= +static void hdef_init(void) +{@+ int i; + @@; + @@; + @@; + @@; + @@; +#if 0 + overfull_rule=0; /* no overfull rules please */ +#endif +} +@ + +After all definitions are ready, we write them using the function +|hput_definitions|. When we output the definitions, +we have to make sure to define references before we use them. +This is achived by using a specific ordering of the +definitions in the function |hput_definitions| and by preventing +the allocation of new definitions as soon as the output of the definition +section has started. The latter has the aditional benefit that the +maximum values do no longer change. + +@= +static void hput_definitions() +/* write the definitions into the definitions buffer */ +{ int i; + uint32_t d, m, s; + hput_definitions_start(); + hput_max_definitions(); + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + @@; + hput_definitions_end(); + hput_range_defs(); /* expects the definitions section to be ended */ + hput_label_defs(); +} +@ + + +In the following, we present for each node type the code to generate +the definitions, using a common schema: We define a data structure +called {\it\dots\_defined}, to hold the definitions; we define, if +applicable, the \TeX-parameters; we add an {\it hget\_\dots\_no} +function to allocate new definitions; and we finish with the code to +output the collected definitions. + +Lets start with the most simple case: integers. + +\subsection{Integers} +\subsubsection{Data} +The data structure to hold the integer definitions is a simple array with |0x100| entries. +A more complex data structure, for example a hash table, could speed up searching for +existing definitions but lets keep things simple for now. + +@= +static int32_t int_defined[0x100]={0}; +@ +\subsubsection{Mapping} +Before we can generate definitions for \TeX-parameters, we have to map \TeX's +parameter numbers to \HINT\ definition numbers. While it seems more convenient here +to have the reverse mapping, we need the mapping only once to record parameter definitions, +but we will need it repeatedly in the function |hdef_param_node| and the overhead here does +not warant having the mapping in both directions. + +@= +static const int hmap_int[] ={@/ +pretolerance_no, /* |pretolerance_code| 0 */ +tolerance_no, /* |tolerance_code| 1 */ +line_penalty_no, /* |line_penalty_code| 2 */ +hyphen_penalty_no, /* |hyphen_penalty_code| 3 */ +ex_hyphen_penalty_no, /* |ex_hyphen_penalty_code| 4 */ +club_penalty_no, /* |club_penalty_code| 5 */ +widow_penalty_no, /* |widow_penalty_code| 6 */ +display_widow_penalty_no, /* |display_widow_penalty_code| 7 */ +broken_penalty_no, /* |broken_penalty_code| 8 */ +-1, /* |bin_op_penalty_code| 9 */ +-1, /* |rel_penalty_code| 10 */ +pre_display_penalty_no, /* |pre_display_penalty_code| 11 */ +post_display_penalty_no, /* |post_display_penalty_code| 12 */ +inter_line_penalty_no, /* |inter_line_penalty_code| 13 */ +double_hyphen_demerits_no, /* |double_hyphen_demerits_code| 14 */ +final_hyphen_demerits_no, /* |final_hyphen_demerits_code| 15 */ +adj_demerits_no, /* |adj_demerits_code| 16 */ +-1, /* |mag_code| 17 */ +-1, /* |delimiter_factor_code| 18 */ +looseness_no, /* |looseness_code| 19 */ +time_no, /* |time_code| 20 */ +day_no, /* |day_code| 21 */ +month_no, /* |month_code| 22 */ +year_no, /* |year_code| 23 */ +-1, /* |show_box_breadth_code| 24 */ +-1, /* |show_box_depth_code| 25 */ +-1, /* |hbadness_code| 26 */ +-1, /* |vbadness_code| 27 */ +-1, /* |pausing_code| 28 */ +-1, /* |tracing_online_code| 29 */ +-1, /* |tracing_macros_code| 30 */ +-1, /* |tracing_stats_code| 31 */ +-1, /* |tracing_paragraphs_code| 32 */ +-1, /* |tracing_pages_code| 33 */ +-1, /* |tracing_output_code| 34 */ +-1, /* |tracing_lost_chars_code| 35 */ +-1, /* |tracing_commands_code| 36 */ +-1, /* |tracing_restores_code| 37 */ +-1, /* |uc_hyph_code| 38 */ +-1, /* |output_penalty_code| 39 */ +-1, /* |max_dead_cycles_code| 40 */ +hang_after_no, /* |hang_after_code| 41*/ +floating_penalty_no /* |floating_penalty_code| 42*/ +}; + + +@ +\subsubsection{Parameters} +Now we can generate the definitions for integer parameters: + +@= + int_defined[zero_int_no]=0; + for (i=pretolerance_code; i<=hang_after_code;i++) + if ( hmap_int[i]>=0) int_defined[hmap_int[i]]=int_par(i); + max_ref[int_kind]=MAX_INT_DEFAULT; +@ + + +\subsubsection{Allocation} +The function |hget_int_no| tries to allocate a predefined integer number; +if not successful, it returns $-1$. + +@= +static int hget_int_no(int32_t n) +{ int i; + int m =max_ref[int_kind]; + for (i=0; i<=m; i++) + if (n== int_defined[i]) return i; + if (m<0xFF && section_no==2) + { m=++max_ref[int_kind]; int_defined[m]=n; return m; } + else + return -1; +} +@ + +\subsubsection{Output} +Before we give the code to output an integer definition, we declare a macro that +is usefull for all the definitions. |HPUTDEF| takes a function |F| and a reference number |R|. +It is assumed that |F| writes a definition into the output and returns a tag. The macro +will then add the reference number and both tags to the output. +@= +#define HPUTDEF(F,R) \ + { uint32_t _p; \ + uint8_t _t; \ + HPUTNODE; /* allocate */ \ + _p=hpos-hstart; \ + HPUT8(0); /* tag */ \ + HPUT8(R); /* reference */ \ + _t=F; \ + hstart[_p]=_t; DBGTAG(_t,hstart+_p); \ + DBGTAG(_t,hpos); HPUT8(_t); \ + } +@ + +Definitions are written to the output only if they differ from Hi\TeX's built in defaults. +@= + DBG(DBGDEF,"Maximum int reference: %d\n",max_ref[int_kind]); + for (i=max_fixed[int_kind]+1;i<=max_default[int_kind]; i++) + { if (int_defined[i]!=int_defaults[i])@/ + HPUTDEF(hput_int(int_defined[i]),i); + } + for (;i<=max_ref[int_kind]; i++)@/ + HPUTDEF(hput_int(int_defined[i]),i); +@ + + +\subsection{Dimensions} +We proceed as we did for integers, starting with the array that holds the defined dimensions. +\subsubsection{Data} +@= +static scaled dimen_defined[0x100]={0}; +@ +\subsubsection{Mapping} +@= +static const int hmap_dimen[] ={@/ + -1, /* |par_indent_code| 0 */ + -1, /* |math_surround_code| 1 */ + line_skip_limit_no, /* |line_skip_limit_code| 2 */ + hsize_dimen_no, /* |hsize_code| 3 */ + vsize_dimen_no, /* |vsize_code| 4 */ + max_depth_no, /* |max_depth_code| 5 */ + split_max_depth_no, /* |split_max_depth_code| 6 */ + -1, /* |box_max_depth_code| 7 */ + -1, /* |hfuzz_code| 8 */ + -1, /* |vfuzz_code| 9 */ + -1, /* |delimiter_shortfall_code| 10 */ + -1, /* |null_delimiter_space_code| 11 */ + -1, /* |script_space_code| 12 */ + -1, /* |pre_display_size_code| 13 */ + -1, /* |display_width_code| 14 */ + -1, /* |display_indent_code| 15 */ + -1, /* |overfull_rule_code| 16 */ + hang_indent_no, /* |hang_indent_code| 17 */ + -1, /* |h_offset_code| 18 */ + -1, /* |v_offset_code| 19 */ + emergency_stretch_no /* |emergency_stretch_code| 20 */ +}; +@ + +\subsubsection{Parameters} +@= + dimen_defined[zero_dimen_no]=0; + for (i=par_indent_code; i<=emergency_stretch_code;i++) + if ( hmap_dimen[i]>=0) dimen_defined[hmap_dimen[i]]=dimen_par(i); + dimen_defined[hsize_dimen_no]=page_h; + dimen_defined[vsize_dimen_no]=page_v; + dimen_defined[quad_no]=quad(cur_font); + dimen_defined[math_quad_no]=math_quad(text_size); + max_ref[dimen_kind]=MAX_DIMEN_DEFAULT; +@ + +\subsubsection{Allocation} +@= +static int hget_dimen_no(scaled s) +/* tries to allocate a predefined dimension number in the range 0 to 0xFF + if not successful return -1 */ +{ int i; + int m =max_ref[dimen_kind]; + for (i=0; i<=m; i++) + if (s== dimen_defined[i]) return i; + if (m<0xFF && section_no==2) + { m=++max_ref[dimen_kind]; dimen_defined[m]=s; return m; } + else + return -1; +} +@ + +\subsubsection{Output} +@= + DBG(DBGDEF,"Maximum dimen reference: %d\n",max_ref[dimen_kind]); + for (i=max_fixed[dimen_kind]+1;i<=max_default[dimen_kind]; i++) + { if (dimen_defined[i]!=dimen_defaults[i]) + HPUTDEF(hput_dimen(dimen_defined[i]),i); + } + for (;i<=max_ref[dimen_kind]; i++) + HPUTDEF(hput_dimen(dimen_defined[i]),i); +@ + + +\subsection{Extended Dimensions} + +\subsubsection{Data} +@= +static struct { +scaled w,h,v; } xdimen_defined[0x100]; +@ + +\subsubsection{Initialization} +@= + for (i=0; i<=max_fixed[xdimen_kind]; i++) + { xdimen_defined[i].w = xdimen_defaults[i].w; + xdimen_defined[i].h = ONE*xdimen_defaults[i].h; + xdimen_defined[i].v = ONE*xdimen_defaults[i].v; + } +@ + +\subsubsection{Allocation} +To obtain a reference number for an extended dimension, we search the +array and if no match was found, we allocate a new entry, +reallocating the array if needed. +We use the variable |rover| to mark the place where the +last entry was inserted, because +quite often we repeatedly search for the same values. + +@= +int hget_xdimen_no(pointer p) +{ int i; + for (i=0;i<=max_ref[xdimen_kind];i++) + { if (xdimen_defined[i].w== xdimen_width(p) && + xdimen_defined[i].h== xdimen_hfactor(p) && + xdimen_defined[i].v== xdimen_vfactor(p)) + return i; + } + if (section_no!=2) return -1; + if (i>=0x100) return -1; + max_ref[xdimen_kind]=i; + xdimen_defined[i].w= xdimen_width(p); + xdimen_defined[i].h= xdimen_hfactor(p); + xdimen_defined[i].v= xdimen_vfactor(p); + return i; +} +@ + +@= +pointer new_xdimen(scaled w, scaled h, scaled v) +{ pointer p=get_node(xdimen_node_size); + type(p)=whatsit_node;subtype(p)=xdimen_node; + xdimen_width(p)=w; + xdimen_hfactor(p)=h; + xdimen_vfactor(p)=v; + return p; +} +@ + +\subsubsection{Output} +@= + DBG(DBGDEF,"Maximum xdimen reference: %d\n",max_ref[xdimen_kind]); + for (i=max_fixed[xdimen_kind]+1;i<=max_default[xdimen_kind]; i++) + { xdimen_t x; + x.w=xdimen_defined[i].w; + x.h=xdimen_defined[i].h/(double)ONE; + x.v=xdimen_defined[i].v/(double)ONE; + + if (x.w!=xdimen_defaults[i].w || + x.h!=xdimen_defaults[i].h || + x.v!=xdimen_defaults[i].v) + HPUTDEF(hput_xdimen(&x),i); + } + for (;i<=max_ref[xdimen_kind]; i++) + { xdimen_t x; + x.w=xdimen_defined[i].w; + x.h=xdimen_defined[i].h/(double)ONE; + x.v=xdimen_defined[i].v/(double)ONE; + HPUTDEF(hput_xdimen(&x),i); + } +@ + + +@ +\subsection{Glues} +In general there are two choices on how to store a definition: We can use the data structures used by \TeX\ +or we can use the data structures defined by \HINT. If we are lucky, both of them are the same +as we have seen for integers and dimensions. For extended dimensions, we had to use the \HINT\ data type +|xdimen_t| because \TeX\ has no corresponding data type and uses only reference numbers. +In the case of glue, we definitely have a choice. We decide to use \TeX's pointers to glue specifications +in the hope to save some work when comparing glues for equality, because \TeX\ already reuses +glue specifications and often a simple comparison of pointers might suffice. +\subsubsection{Data} +@= +static pointer glue_defined[0x100]; +@ +\subsubsection{Mapping} +@= +static int hmap_glue[] ={ + +line_skip_no, /* |line_skip_code| 0 */ +baseline_skip_no, /* |baseline_skip_code| 1 */ +-1, /* |par_skip_code| 2 */ +above_display_skip_no, /* |above_display_skip_code| 3 */ +below_display_skip_no, /* |below_display_skip_code| 4 */ +above_display_short_skip_no, /* |above_display_short_skip_code| 5 */ +below_display_short_skip_no, /* |below_display_short_skip_code| 6 */ +left_skip_no, /* |left_skip_code| 7 */ +right_skip_no, /* |right_skip_code| 8 */ +top_skip_no, /* |top_skip_code| 9 */ +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 */ +par_fill_skip_no /* |par_fill_skip_code| 14 */ +}; +@ +\subsubsection{Parameters} +@= + glue_defined[zero_skip_no]=zero_glue; incr(glue_ref_count(zero_glue)); + for (i=line_skip_code; i<=par_fill_skip_code;i++) + if ( hmap_glue[i]>=0) + { glue_defined[hmap_glue[i]]=glue_par(i); incr(glue_ref_count(glue_par(i)));} + max_ref[glue_kind]=MAX_GLUE_DEFAULT; +@ + + +\subsubsection{Allocation} +Next we define some auxiliar routines to compare glues for equality and to convert glues beween the different representations. + +@= +int glue_spec_equal(pointer p, pointer q) +{ return (width(q)==width(p) && stretch(q)==stretch(p) && shrink(q)==shrink(p) && + (stretch_order(q)==stretch_order(p) || stretch(q)==0) && + (shrink_order(q)==shrink_order(p)|| shrink(q)==0)); +} + +int glue_equal(pointer p, pointer q) +{ return p==q || glue_spec_equal(p,q); +} + +int glue_t_equal(glue_t *p, glue_t *q) +{ return(p->w.w==q->w.w && p->w.h==q->w.h && p->w.v==q->w.v && + p->p.f== q->p.f && p->m.f==q->m.f && + (p->p.o==q->p.o || p->p.f==0.0) && + (p->m.o==q->m.o || q->m.f==0.0)); +} + +@ + +To find a matching glue we make two passes over the defined glues: +on the first pass we just compare pointers and on the second pass +we also compare values. An alternative approach to speed up searching +is used in section~\secref{paramlist} below. + +@= +static int hget_glue_no(pointer p) +{ static int rover=0; + int i; + if (p==zero_glue) return zero_skip_no; + for(i=0; i<= max_ref[glue_kind] ; i++) + { if (p==glue_defined[rover]) return rover; + else if (rover==0) + rover=max_ref[glue_kind]; + else + rover--; + } + for(i=0; i<= max_ref[glue_kind] ; i++) + { pointer q=glue_defined[rover]; + if (glue_spec_equal(p,q)) + return rover; + else if (rover==0) + rover=max_ref[glue_kind]; + else + rover--; + } + if (max_ref[glue_kind]<0xFF && section_no==2) + { rover=++max_ref[glue_kind]; + glue_defined[rover]=p; + incr(glue_ref_count(p)); + DBG(DBGDEF,"Defining new glue %d\n",rover); + return rover; + } + else + return -1; +} +@ +\subsubsection{Output} +@= + DBG(DBGDEF,"Maximum glue reference: %d\n",max_ref[glue_kind]); + for (i=max_fixed[glue_kind]+1;i<=max_default[glue_kind]; i++) + { glue_t g; + to_glue_t(glue_defined[i],&g); + if (!glue_t_equal(&g,&glue_defaults[i])) + HPUTDEF(hput_glue(&g),i); + } + for (;i<=max_ref[glue_kind]; i++) + HPUTDEF(hout_glue_spec(glue_defined[i]),i); +@ + +The above code uses the following conversion routine. +While \HINT\ supports glue that depends on {\tt hsize} and {\tt vsize}, +this is currently not supported by Hi\TeX. +Future versions of Hi\TeX\ should extend glue spec nodes (and kern nodes) +by fields for |hfactor| and |vfactor| which are zero by default. +This would leave most parts of \TeX\ unchanged. +As a work-around one can combine a box with an extended dimension with +a regular glue or kern. +% Care should be taken for the statically allocated glue specs +% deallocation of glue specs is relatively simple +% extended glue and kern values can be restricted to non math mode +@= +void to_glue_t(pointer p, glue_t *g) +{ g->w.w=width(p); + g->w.h=g->w.v=0.0; + g->p.f=stretch(p)/(double)ONE; g->p.o= stretch_order(p); + g->m.f=shrink(p)/(double)ONE; g->m.o= shrink_order(p); +} +@ + +\subsection{Baseline Skips} +TeX's baseline nodes just store a baseline skip reference number. +We have seen this sitation before when dealing with extended dimensions +and the solution here is the same: a dynamicaly allocated array. +\subsubsection{Data} +@= +typedef struct { + pointer ls, bs; /* line skip and baselineskip gluespecs */ + scaled lsl; /* lineskip limit */ +} bl_defined_t; + +static bl_defined_t *bl_defined=NULL; +static int bl_used=0,bl_allocated=0; +@ + +\subsubsection{Initialization} +The zero baseline skip is predefined which prevents an ambiguous info value of zero +in a baseline node. + +@= + bl_allocated=8; + ALLOCATE(bl_defined,bl_allocated,bl_defined_t); + bl_defined[zero_baseline_no].bs=zero_glue; incr(glue_ref_count(zero_glue)); + bl_defined[zero_baseline_no].ls=zero_glue; incr(glue_ref_count(zero_glue)); + bl_defined[zero_baseline_no].lsl=0; + bl_used= MAX_BASELINE_DEFAULT+1; + max_ref[baseline_kind]= MAX_BASELINE_DEFAULT; +@ +\subsubsection{Allocation} +@= +int hget_baseline_no(pointer bs, pointer ls, scaled lsl) +{ + static int rover=0; + int i; + for(i=0; i< bl_used; i++) /* search for an existing spec */ + { bl_defined_t *q=&(bl_defined[rover]); + if (glue_equal(bs,q->bs) && glue_equal(ls,q->ls) && lsl==q->lsl) + return rover; + else if (rover==0) + rover=bl_used-1; + else + rover--; + } + if (bl_used>=bl_allocated) + RESIZE(bl_defined,bl_allocated, bl_defined_t); + rover=bl_used++; + if (rover<0x100 && section_no==2) max_ref[baseline_kind]=rover; + if (glue_equal(bs,zero_glue)) + { bl_defined[rover].bs=zero_glue; incr(glue_ref_count(zero_glue)); } + else + { bl_defined[rover].bs=bs; incr(glue_ref_count(bs));} + if (glue_equal(ls,zero_glue)) + { bl_defined[rover].ls=zero_glue; incr(glue_ref_count(zero_glue)); } + else + { bl_defined[rover].ls=ls; incr(glue_ref_count(ls));} + bl_defined[rover].lsl=lsl; + return rover; +} + +@ +\subsubsection{Output} + +The following routine does not allocate a new glue definition, because the +baselinedefinitions are output after the glue definitions. This is not perfect. +@= +static uint8_t hout_glue_spec(pointer p); +uint8_t hout_baselinespec(int n) +{ info_t i=b000; + pointer p; + scaled s; + s=bl_defined[n].lsl; + if (s!=0) {HPUT32(s); i|=b001;} + p=bl_defined[n].bs; + if (p!=zero_glue) +{ uint8_t *pos; + uint8_t tag; + HPUTNODE; /* allocate */ + pos=hpos; + hpos++; /* tag */ + tag=hout_glue_spec(p); + *pos=tag; DBGTAG(tag,pos); + DBGTAG(tag,hpos); HPUT8(tag); + i|=b100; +} + p=bl_defined[n].ls; + if (p!=zero_glue) +{ uint8_t *pos; + uint8_t tag; + HPUTNODE; /* allocate */ + pos=hpos; + hpos++; /* tag */ + tag=hout_glue_spec(p); + *pos=tag; DBGTAG(tag,pos); + DBGTAG(tag,hpos); HPUT8(tag); + i|=b010; +} + return TAG(baseline_kind,i); +} +@ + +@= + DBG(DBGDEF,"Defining %d baseline skips\n",max_ref[baseline_kind]); + for (i=1;i<=max_ref[baseline_kind]; i++) + { uint32_t pos=hpos-hstart; + uint8_t tag; + hpos++; /* space for the tag */ + HPUT8(i); /* reference */ + tag=hout_baselinespec(i); + hstart[pos]=tag; + HPUT8(tag); + } +@ +\subsubsection{Printing} +The following function is needed in Hi\TeX\ to produce debugging output if needed. +@= +void print_baseline_skip(int i) +{ if (0<=i && i < bl_used) + { print_spec(bl_defined[i].bs,0); print_char(','); + print_spec(bl_defined[i].ls,0); print_char(','); + print_scaled(bl_defined[i].lsl); + } + else + print("unknown"); +} +@ + + + +\subsection{Discretionary breaks} +\subsubsection{Data} +For discretionary breaks, we use again the pointer representation. +@= +static pointer dc_defined[0x100]; +@ +\subsubsection{Allocation} +There are no predefined discretionary breaks and so we start with +three auxiliar functions and the +function to get a ``disc'' number. + +The first two routines are used to compare discretionary breaks +in order to reuse already defined disc numbers. +The pre and post break lists must consist entirely of character, +kern, box, rule, and ligature nodes. +Unfortunately a box node might contain all kinds of nodes +and its content might be huge and deeply nested. +The following routine will not make a complete comparison but will give +up if the box content is ``too complex''. + +@= +static bool list_equal(pointer p, pointer q); +static bool node_equal(pointer p, pointer q) +{ if (is_char_node(p) && is_char_node(q) && + font(p)==font(q) && character(p)==character(q)) + return true; + if (!is_char_node(p) && !is_char_node(q)) + { if (type(p)!=type(q)) return false; + if (type(p)==kern_node && + subtype(p)==subtype(q) && width(p)==width(q)) + return true; + if (type(p)==ligature_node && + character(lig_char(p)) == character(lig_char(q)) && + font(lig_char(p)) == font(lig_char(q))) + return true; + if (type(p)==rule_node && + width(p)==width(q) && height(p)==height(q) && depth(p)==depth(q)) + return true; + if ((type(p)==hlist_node || type(p)==vlist_node) && + width(p)==width(q) && height(p)==height(q) && depth(p)==depth(q) && + shift_amount(p)==shift_amount(q) && + glue_sign(p) == glue_sign(q) && + glue_order(p) == glue_order(q) && + glue_set(p) == glue_set(q) && + list_equal(list_ptr(p),list_ptr(q))) + return true; + } + return false; +} + +static bool list_equal(pointer p, pointer q) +{@+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); + } +} + +static pointer copy_disc_node(pointer p) +{ pointer q; + q=get_node(small_node_size); + pre_break(q)=copy_node_list(pre_break(p)); + post_break(q)=copy_node_list(post_break(p)); + type(q)=type(p); + subtype(q)=subtype(p); /* replace count and explicit bit */ + return q; +} +@ + +@= +int hget_disc_no(pointer p) +{ + static int rover=0; + int i; + for(i=0; i<= max_ref[disc_kind]; i++) + { pointer q=dc_defined[rover]; + if ( is_auto_disc(p)==is_auto_disc(q) && replace_count(p)==replace_count(q) && + list_equal(pre_break(p),pre_break(q)) && + list_equal(post_break(p),post_break(q))) + return rover; + else if (rover==0) + rover=max_ref[disc_kind]; + else + rover--; + } + if (max_ref[disc_kind]>=0xFF || section_no!=2) return -1; + + rover=++max_ref[disc_kind]; + dc_defined[rover]=copy_disc_node(p); + @@; + return rover; +} +@ + +When we allocate disc numbers we might have fonts inside the pre- +or post-break list, that never show up anywhere else in the content. +These fonts would then be undefined once we start the definition section. +So we have to make sure, all necessary fonts get defined. + +@= +ensure_font_no(pre_break(p)); +ensure_font_no(post_break(p)); +@ + +\subsubsection{Output} +@= + DBG(DBGDEF,"Maximum disc reference: %d\n",max_ref[disc_kind]); + for (i=0;i<=max_ref[disc_kind]; i++) + HPUTDEF(hout_disc(dc_defined[i]),i); +@ + + + +\subsection{Parameter Lists}\label{paramlist} +\subsubsection{Data} +We store predefined parameter lists in a hash table in order to speed up +finding existing parameter lists. The parameter list itself is stored as +a byte sequence using the short \HINT\ file format. +We link the table entries in order of increasing reference numbers to be able +to output them in a more ``orderly'' fashion. + +@= + +#define PLH_SIZE 313 /* a prime number $\approx2^8\times 1.2$. */ + +struct {int l; /* link */ + uint32_t h; /* hash */ + uint32_t n; /* number */ + uint32_t s; /* size */ + uint8_t *p; /* pointer */} pl_defined[PLH_SIZE]={{0}}; +int pl_head=-1, *pl_tail=&pl_head; +@ + +\subsubsection{Allocation} +Next we define three short auxiliar routines and the |hget_param_list_no| function. + +@= + +static uint32_t hparam_list_hash(list_t *l) +{ uint32_t h=0; + uint32_t i; + for (i=0;is;i++) + h=3*h+hstart[l->p+i]; + return i; +} + +static bool pl_equal(list_t *l, uint8_t *p) +{ uint8_t *q=hstart+l->p; + uint32_t i; + for (i=0; is; i++) + if (q[i]!=p[i]) return false; + return true; +} + +static void pl_copy(list_t *l, uint8_t *p) +{ uint8_t *q=hstart+l->p; + memcpy(p,q,l->s); +} + +int hget_param_list_no(list_t *l) +{ uint32_t h; + int i; + if (l->s<=0) return -1; + h= hparam_list_hash(l); + i = h%PLH_SIZE; + while (pl_defined[i].p!=NULL) + { if (pl_defined[i].h==h && pl_equal(l,pl_defined[i].p)) + return pl_defined[i].n; + i=i+199; /* some other prime */ + if (i>=PLH_SIZE) i=i-PLH_SIZE; + } + if (max_ref[param_kind]>=0xFF || section_no!=2) return -1; + pl_defined[i].n=++max_ref[param_kind]; + *pl_tail=i; pl_tail=&(pl_defined[i].l); + pl_defined[i].l=-1; + pl_defined[i].h=h; + pl_defined[i].s=l->s; + ALLOCATE(pl_defined[i].p,l->s,uint8_t); + pl_copy(l,pl_defined[i].p); + return pl_defined[i].n; +} +@ + +\subsubsection{Output} +To output parameter lists, we need a function to output a parameter node: + +@= +void hdef_param_node(int ptype, int pnumber,int pvalue) +{ + if (ptype==int_type) + { if (pvalue==int_defined[hmap_int[pnumber]]) return; + else HPUTDEF(hput_int(pvalue),hmap_int[pnumber]); + } + else if (ptype==dimen_type) + { if (pvalue==dimen_defined[hmap_dimen[pnumber]]) return; + else HPUTDEF(hput_dimen(pvalue),hmap_dimen[pnumber]); + } + else if (ptype==glue_type) + { if (glue_equal(pvalue,glue_defined[hmap_glue[pnumber]])) return; + else HPUTDEF(hout_glue_spec((pointer)pvalue),hmap_glue[pnumber]); + } + else QUIT("Unexpected parameter type %d",ptype); +} +@ + + +Now we use the linked list starting with |pl_head| to output the predefined +parameter lists sorted by their reference number. + + @= + DBG(DBGDEF,"Defining %d parameter lists\n",max_ref[param_kind]+1); + for (i=pl_head;i>=0;i=pl_defined[i].l) + { int j; + DBG(DBGDEF,"Defining parameter list %d, size 0x%x\n",i,pl_defined[i].s); + j=hsize_bytes(pl_defined[i].s); + HPUTX(1+1+j+1+pl_defined[i].s+1+j+1); + HPUTTAG(param_kind,j+1); + HPUT8(pl_defined[i].n); + hput_list_size(pl_defined[i].s,j); + HPUT8(0x100-j); + memcpy(hpos,pl_defined[i].p,pl_defined[i].s); + hpos=hpos+pl_defined[i].s; + HPUT8(0x100-j); + hput_list_size(pl_defined[i].s,j); + HPUTTAG(param_kind,j+1); + } +@ + +\subsection{Fonts} + +It seems I need: (see email by Karl) + + +|kpse_find_file(name,kpse_fontmap_format,false);| +with name "ps2pk.map" or "psfonts.map" or "ttfonts.map" or cmfonts.map +to get from the tfm name the postscript name. +then I get the |psfont_name| ususaly its the same font name +with .pfb appended +so I can skip it +%#ifdef HAVE_KPSE_ENC_FORMATS +% enc_file=kpse_find_file(encoding,kpse_enc_format,false); +%#else +% enc_file=kpse_find_file(encoding,kpse_tex_ps_header_format,false); +%#endif +%reading the encoding is found in dvipng/dvipnd-src/enc.c + + + +\subsubsection{Data} +To store a font definition, we define the data type |font_t| +and an array |hfonts| of pointers indexed by \HINT\ font numbers. +To map \HINT\ font numbers to \TeX\ font numbers, the |font_t| contains +the |i| field; to map \TeX\ font numbers to \HINT\ font numbers, +we use the array |hmap_font|. + +@= +#define MAX_FONTS 0x100 + +typedef struct { + uint8_t i; /* the \TeX\ font number */ + pointer g; /* space glue */ + pointer h; /* default hyphen */ + pointer p[MAX_FONT_PARAMS]; /* font parameters */ + uint16_t m; /* section number of font metric file */ + uint16_t y; /* section number of font glyph file */ +} font_t; + +static font_t *hfonts[MAX_FONTS]={NULL}; +static int hmap_font[MAX_FONTS]; +@ + + + +@= + for (i=0;i<0x100;i++) hmap_font[i]=-1; + max_ref[font_kind]=-1; +@ + +\subsubsection{Allocation} +Allocation of a |font_t| record takes place when we translate a \TeX\ font +number to a \HINT\ font number using the function |hget_font_no|, and while +doing so discover that the corresponding \HINT\ font number does not yet exist. +Because the |font_t| structure must be initialized after allocating it, +we start with some auxiliar routines for that purpose. + +@= +static pointer find_space_glue(internal_font_number f) +{@+font_index @!main_k; + pointer main_p=font_glue[f]; +if (main_p==null) + {@+main_p=new_spec(zero_glue);main_k=param_base[f]+space_code; + width(main_p)=font_info[main_k].sc; /*that's |space(f)|*/ + stretch(main_p)=font_info[main_k+1].sc; /*and |space_stretch(f)|*/ + shrink(main_p)=font_info[main_k+2].sc; /*and |space_shrink(f)|*/ + font_glue[f]=main_p; + } + return main_p; +} +static pointer hget_font_space(uint8_t f) +{ pointer p; + if (space_skip==zero_glue) + p = find_space_glue(f); + else + p=glue_par(space_skip_code); + add_glue_ref(p); + return p; +} + + +static pointer hget_font_hyphen(uint8_t f) +{ pointer p; + int c; + p=new_disc(); + c= hyphen_char[f]; + if (c >= 0 && c < 256) pre_break(p)=new_character(f, c); + return p; +} + + +static void hdef_font_params(pointer p[MAX_FONT_PARAMS]) +{ /* used only for texts */ +} +@ +\goodbreak + +In the following code, |f| is a \TeX\ internal font number +and |g| is the corresponding \HINT\ font number. +\TeX's null-font, a kind of undefined font containing no characters +is replaced by \HINT's font number zero. Actually the nullfont should +never appear in the output, but if it does so, either an error message +or a more sensible replacement font might be in order. + +@= + +static char *hfind_glyphs(char *filename) +{ char *fname=NULL; + kpse_glyph_file_type file_ret; + fname=kpse_find_file(filename,kpse_type1_format,true); + if (fname==NULL) fname=kpse_find_file(filename,kpse_truetype_format,true); + if (fname==NULL) fname=kpse_find_file(filename,kpse_opentype_format,true); + if (fname==NULL) fname = kpse_find_glyph(filename, option_dpi,kpse_pk_format, &file_ret); + if (fname==NULL) + fprintf(stderr,"Unable to find glyph data for font %s\n",filename),exit(1); + return fname; +} + +static uint8_t hget_font_no(uint8_t f) + +{ int g; + char *n,*fn; + int l; + if (f==0) + { DBG(DBGFONT,"TeX nullfont -> 0\n"); + return 0;@+ + } + g=hmap_font[f]; + DBG(DBGFONT,"Mapping TeX font %d->%d\n",f,g); + if (g>=0) return g; + DBG(DBGDEF,"New TeX font %d\n",f); + if (max_ref[font_kind]>=0x100) + QUIT("too many fonts in use"); + g = ++(max_ref[font_kind]); + ALLOCATE(hfonts[g],1,font_t); + hfonts[g]->i=f; + hmap_font[f]=g; + hfonts[g]->g=hget_font_space(f); + hfonts[g]->h=hget_font_hyphen(f); + pack_file_name(font_name[f], "",".tfm"); + n = kpse_find_tfm((char*)name_of_file+1); + if (n==NULL) + QUIT("Unable to find .tfm file for font %s",(char*)name_of_file+1); + hfonts[g]->m= hnew_file_section(n); + free(n); + pack_file_name(font_name[f], "",""); + n= hfind_glyphs((char*)name_of_file+1); + if (n==NULL) + QUIT("Unable to find glyph file for font %s",(char*)name_of_file+1); + hfonts[g]->y= hnew_file_section(n); + free(n); + return g; +} +@ + +Surprisingly, not all characters that occur in a \HINT\ file are inside the +content section; some characters might hide in the definition section +inside the pre- or post-break list of a predefined discretionary break. +To make sure that the fonts necessary for these characters are included +in the final \HINT\ file, we check these lists to make sure all \TeX\ font +numbers have a corresponting \HINT\ font number. + +@= +static void ensure_font_no(pointer p) +{ while (p!=null) + { if (is_char_node(p)) + hget_font_no(font(p)); + else if (type(p)==hlist_node||type(p)==vlist_node) + ensure_font_no(list_ptr(p)); + p=link(p); + } +} +@ + +\subsubsection{Output} +@= +{ int f; + DBG(DBGDEF,"Defining %d fonts\n",max_ref[font_kind]+1); + for (f=0;f<=max_ref[font_kind];f++) + { font_t *hf=hfonts[f]; + internal_font_number g=hf->i; + uint32_t pos=hpos-hstart; + info_t i= b000; + DBG(DBGDEF,"Defining font %d size 0x%x\n",f,font_size[g]); + hpos++; HPUTNODE; /* space for the tag and the node */ + HPUT8(f); /* reference */ + hout_string(font_id_text(g)); + if(font_size[g]>0) HPUT32(font_size[g]); + else HPUT32(font_dsize[g]); + HPUT16(hf->m);HPUT16(hf->y); + DBG(DBGDEF,"Defining font space\n"); + HPUTCONTENT(hout_glue_spec,hf->g); + DBG(DBGDEF,"Defining font hyphen\n"); + HPUTCONTENT(hout_disc,hf->h); + hdef_font_params(hf->p); + DBG(DBGDEF,"End of font %d\n",f); + hput_tags(pos,TAG(font_kind,i)); + } +} +@ + +We used the following function to write a \TeX\ string to the \HINT\ file: +@= + +void hout_string(int s) +{ pool_pointer j; + uint8_t c; + j= str_start[s]; + while(j= 0x7F) + { char str[4]; + snprintf(str,4,"%%%02X",c);/* convert to printable ASCII */ + HPUTX(3); + HPUT8(str[0]); HPUT8(str[1]); HPUT8(str[2]); + } + else + { HPUTX(1); + HPUT8(c); + } + } + HPUT8(0); +} +@ + + +We used the following macro to add tags around the font glue and the font hyphen: + + +@= + +#define HPUTCONTENT(F,D) \ + { uint8_t *_p; \ + uint8_t _t; \ + HPUTNODE; /* allocate */ \ + _p=hpos++; /* tag */ \ + _t=F(D); \ + *_p=_t; DBGTAG(_t,_p); \ + DBGTAG(_t,hpos); HPUT8(_t); \ + } +@ + +\subsection{Labels} +The only label that must exist always is the zero label used +to mark the ``home'' position of a document. + +\subsubsection{Initialization} +We allocate the zero label with the first call to |next_label| +and initialize is with the value from |label_defaults|. +We then make sure it can be found under the name ``HINT.home''. + +@= +{ char nom[]="HINT.home"; + unsigned int h=name_hash(nom)%LABEL_HASH; + int i=insert_hash(h,0,nom); + if (i!=zero_label_no) + QUIT("Trying to allocate the zero label, got %d",i); + labels[zero_label_no]=label_defaults[zero_label_no]; + labels[zero_label_no].next=first_label; + first_label=zero_label_no; + DBG(DBGLABEL,"Defining zero label: pos=0x%x\n",labels[zero_label_no].pos); +} +@ + +\subsection{Page Templates} + +Once we start producing content nodes, we update the maximum numbers +of page templates and streams from |max_page| and |max_stream|. +These values might have changed because the templates were stored in a +format file. + +@= +max_ref[page_kind]=max_page; +max_ref[stream_kind]=max_stream; +@ + + +\subsubsection{Output} + +As part of a page template, we will see stream insertion nodes. +When we encounter an |stream_node| inside a template definition, +we output a stream insertion point. + +@= + case stream_node: + HPUT8(setstream_number(p)); + tag=TAG(stream_kind,b100); + break; +@ + + +@= + DBG(DBGDEF,"Maximum page template reference: %d\n",max_page); + { pointer t; + for (t=link(setpage_head);t!=null;t=link(t)) + { uint32_t pos=hpos-hstart; + DBG(DBGDEF,"Defining page template %d\n",setpage_number(i));@/ + hpos++; HPUTNODE; /* space for the tag and the node */ + HPUT8(setpage_number(t)); + hout_string(setpage_name(t)); + HPUT8(setpage_priority(t)); + hout_glue_node(setpage_topskip(t)); + hput_dimen(setpage_depth(t)); + hout_xdimen_node(setpage_height(t)); + hout_xdimen_node(setpage_width(t)); + hout_list_node2(setpage_list(t)); + @@; + hput_tags(pos,TAG(page_kind,0)); + } +} +@ +As part of the output of page template definitions, we output +stream definitions: + +@= +{ pointer p,q; + p = setpage_streams(t); + while (p!=null) + { uint8_t n; + n=setstream_number(p); + DBG(DBGDEF,"Defining stream %d at " SIZE_F "\n",n,hpos-hstart); + HPUTTAG(stream_kind,b100); + HPUT8(n); + hout_xdimen_node(setstream_max(p)); /* maximum height */ + HPUT16(setstream_mag(p)); /* factor */ + HPUT8(setstream_prefered(p)); /* prefered */ + HPUT8(setstream_next(p)); /* next */ + HPUT16(setstream_ratio(p)); /* ratio */ + q=setstream_before(p);setstream_before(p)=null; + hout_list_node2(q);flush_node_list(q); + hout_xdimen_node(setstream_width(p)); + q=setstream_topskip(p); + hout_glue_node(q); delete_glue_ref(q); + q=setstream_after(p);setstream_after(p)=null; + hout_list_node2(q);flush_node_list(q); + q=setstream_height(p); + hout_glue_node(q); delete_glue_ref(q); + HPUTTAG(stream_kind,b100); + p=link(p); + } +} +@ + + + +\section{\HINT\ Content}\label{output} +\TeX\ puts content nodes on the contribution list and once in a while calls |build_page| to +move nodes from the contribution list to the current page. Hi\TeX\ has a special version of +|build_page| that will simply remove nodes from the contribution list and passe them +to the function |hout_node|. + +\noindent +@= +static void hout_node(pointer p) +{ uint32_t pos=hpos-hstart; + uint8_t tag; + HPUTNODE; + hpos++; + if(is_char_node(p)) + @@; + else switch(type(p)) @/ + { @@t\1@>@; + default: + MESSAGE("\nOutput of node type=%d subtype=%d not implemented\n", type(p),subtype(p)); + display_node(p); + MESSAGE("End of node"); + hpos--; + return;@t\2@>@; + } + hput_tags(pos,tag); +} +@ +@= +static void hout_node(pointer p); +@ + +To output \HINT\ nodes, we use the functions defined in {\tt hput.c} from the {\tt shrink} program +(see~\cite{MR:format}). + + +Let's start with character nodes. + +\subsection{Characters} +The processing of a character node consist of three steps: checking for definitions, converting the +\TeX\ node pointed to by |p| to a \HINT\ data type, here a |glyph_t|, and using the +corresponding {\tt hput\_\dots} function to output the node and return the |tag|. +In the following, we will see the same approach in many +small variations for all kinds of nodes. + +@= +{ glyph_t g; + g.f=hget_font_no(font(p)); + g.c = character(p); + tag=hput_glyph(&g); +} +@ + +\subsection{Penalties} +Integer nodes, which as content nodes are used for penalties, come next. +Except for the embedding between |case| and |break|, the +processing of penalty nodes follows the same pattern we have just seen. + @= + case penalty_node: + { int n,i; + i = penalty(p); + if (i>10000) i=10000; + else if (i<-10000) i=-10000; + n=hget_int_no(i); + if (n<0) tag=hput_int(i); + else { HPUT8(n); tag=TAG(penalty_kind,0);} + } + break; +@ + +\subsection{Kerns} +The kern nodes of \TeX\ contain a single dimension and a flag to mark ``explicit'' kerns. + @= + case kern_node: + { int n; + n=hget_dimen_no(width(p)); + if (n<0) + { kern_t k; + k.x=(subtype(p)==explicit); + k.d.w=width(p); + k.d.h=k.d.v=0.0; + tag=hput_kern(&k); + } + else + { HPUT8(n); + if (subtype(p)==explicit) tag=TAG(kern_kind,b100); else tag=TAG(kern_kind,b000); + } + } + break; +@ + +\subsection{Extended Dimensions} +Extended dimensions do not consitute content on their own, but nodes +containing an extended dimension are part of other nodes. Here we +define an auxiliar function that checks for a predefined extended +dimension and if found outputs the reference number and returns false; +otherwise it outputs the extended dimension and returns true. + + +@= +void hout_xdimen_node(pointer p) +{ xdimen_t x; + x.w=xdimen_width(p); + x.h=xdimen_hfactor(p)/(double)ONE; + x.v=xdimen_vfactor(p)/(double)ONE; + hput_xdimen_node(&x); +} +bool hout_xdimen(pointer p) +{int n = hget_xdimen_no(p); + if (n>=0) { HPUT8(n); return false; @+} + else + { hout_xdimen_node(p); return true;@+} +} + +@ + +\subsection{Languages} +We map the language numbers of \TeX\ to \HINT\ language numbers +using the |hlanguage| array. +@= +static struct { + uint8_t n; + str_number s; +} hlanguage[0x100]; +@ + +For any language number of \TeX, the following function returns +the corresponding \HINT\ language number. +Since \TeX\ knowns about a maximum of 255 languages, there is +no need for overflow checking. The next function writes a language +node to the output stream. + +@= +uint8_t hget_language_no(uint8_t n) +{ int i; + for (i=0;i<=max_ref[language_kind]; i++) + if (hlanguage[i].n==n) return i; + i=++max_ref[language_kind]; + hlanguage[i].n=n; + hlanguage[i].s=0; /* language unknown*/ + return i; +} + +uint8_t hout_language(uint8_t n) +{ n=hget_language_no(n); + if (n<7) return TAG(language_kind,n+1); + else + { HPUT8(n); return TAG(language_kind,0); } +} +@ + +After these preparations, the output of a language node is +simple: + +@= +case language_node: + tag=hout_language(what_lang(p)); + break; +@ + +Normaly \TeX\ does not produce an initial language node and +then the language in the \HINT\ file would not be known until +it changes for the first time. + +@= +{ uint32_t pos = hpos-hstart; + hpos++; + hput_tags(pos,hout_language(language)); +} +@ + +\TeX\ offers currently no simple way to obtain a standardized +language identifier for the current language. So if the +string number of the language is zero, we output the string |"unknown"|; +if somehow the language is known, we output the corresponding string +from \TeX's string pool. + +@= + DBG(DBGDEF,"Maximum language reference: %d\n",max_ref[language_kind]); + for (i=max_fixed[language_kind]+1;i<=max_ref[language_kind]; i++) + { HPUTNODE; + HPUT8(TAG(language_kind,0)); + HPUT8(i); + if (hlanguage[i].s==0) + hput_string("unknown"); + else + hout_string(hlanguage[i].s); + HPUT8(TAG(language_kind,0)); + } +@ + + + +\subsection{Mathematics} +\TeX's math nodes have an optional width---a copy of the mathsurround parameter---while +\HINT\ math nodes do not. Therefore we have to add an explicit kern node if +the width is nonzero. We add it before a ``math on'' node or after a ``math off'' +to get the same behavior in respect to line breaking. + +@= + case math_node: + { kern_t k; + k.x=true; + k.d.w=width(p); + k.d.h=k.d.v=0.0; + if (subtype(p)==before) + { tag=TAG(math_kind,b111); + if (width(p)!=0) + { hput_tags(pos,hput_kern(&k)); + pos=hpos-hstart; + HPUTNODE; + hpos++; + } + } + else + { tag=TAG(math_kind,b011); + if (width(p)!=0) + { hput_tags(pos,tag); + pos=hpos-hstart; + HPUTNODE; + hpos++; + tag=hput_kern(&k); + } + } + } + break; +@ + +\subsection{Glue and Leaders} +Because glue specifications and glue nodes are sometimes part of other +nodes, we start with three auxiliar functions: The first simply +converts a Hi\TeX\ glue node into a \HINT\ |glue_t|, outputs it and +returns the tag; the second checks for predefined glues, and the third +outputs a complete glue node including tags. +@= + +static uint8_t hout_glue_spec(pointer p) +{ @+glue_t g; + to_glue_t(p,&g); + return hput_glue(&g);@+ +} + +static uint8_t hout_glue(pointer p) +{ int n; + n = hget_glue_no(p); + if (n<0) + return hout_glue_spec(p); + else + {@+ HPUT8(n); return TAG(glue_kind,0);@+} +} + +static void hout_glue_node(pointer p) +{ uint8_t *pos; + uint8_t tag; + HPUTNODE; /* allocate */ + pos=hpos; + hpos++; /* tag */ + tag=hout_glue(p); + *pos=tag; DBGTAG(tag,pos); + DBGTAG(tag,hpos); HPUT8(tag); +} +@ + +Since \TeX\ implements leaders as a kind of glue, we have one case statement covering glue and leaders. + +@= + case glue_node: + if (subtype(p)< cond_math_glue) /* normal glue */ + tag= hout_glue(glue_ptr(p)); + else if (a_leaders<=subtype(p) && subtype(p)<=x_leaders) /*leaders */ + { hout_glue_node(glue_ptr(p)); + { bool outer_doing_leaders=doing_leaders; + doing_leaders=true; + hout_node(leader_ptr(p)); + doing_leaders=outer_doing_leaders; + } + tag=TAG(leaders_kind,b100|(subtype(p)-a_leaders+1)); + } + else + QUIT("glue subtype %d not implemented\n", subtype(p)); + break; +@ + +\subsection{Discretionary breaks} +Discretionary breaks are needed in font descriptions (see section~\secref{fontdef}). +Therefore we define a function that converts \TeX's |disc_node| pointers +to \HINT's |disc_t|, outputs the discretionary break, and returns the tag. + +@= + +uint8_t hout_disc(pointer p) +{ disc_t h; + h.x=!is_auto_disc(p); + h.r=replace_count(p); + if (h.x) h.r|=0x80; + if (h.r!=0) HPUT8(h.r); + if (pre_break(p)==null && post_break(p)==null) + h.p.s=h.q.s=0; + else + { uint32_t lpos; + lpos=hpos-hstart; + h.p.k=list_kind; + hout_list_node(pre_break(p),lpos,&(h.p)); + if (post_break(p)==null) + h.q.s=0; + else + { uint32_t lpos; + lpos=hpos-hstart; + h.q.k=list_kind; + hout_list_node(post_break(p),lpos,&(h.q)); + } + } + return hput_disc(&h); +} + +@ + + +@= + case disc_node: + { int n; + n=hget_disc_no(p); + if (n<0) + tag=hout_disc(p); + else { HPUT8(n); tag=TAG(disc_kind,0);} + } + break; +@ + +\subsection{Ligatures} +The subtype giving information on left and right boundary characters +is ignored since the \HINT\ viewer will not do ligature or kerning +programms and neither attempt hyphenation. + +@= + case ligature_node: + { lig_t l; + pointer q; + l.f=hget_font_no(font(lig_char(p))); + HPUT8(l.f); + l.l.p=hpos-hstart; + hput_utf8(qo(character(lig_char(p)))); + q=lig_ptr(p); + while(q> null) + { hput_utf8(qo(character(q))); + q=link(q); + } + l.l.s=(hpos-hstart)-l.l.p; + tag=hput_ligature(&l); + } + break; +@ +\subsection{Rules} +@= + case rule_node: + { rule_t r; + if (is_running(height(p))) r.h=RUNNING_DIMEN; else r.h=height(p); + if (is_running(depth(p))) r.d=RUNNING_DIMEN; else r.d=depth(p); + if (is_running(width(p))) r.w=RUNNING_DIMEN; else r.w=width(p); + tag=hput_rule(&r); + } + break; +@ + +\subsection{Boxes} +@= + case hlist_node: + case vlist_node: + if (type(p)==hlist_node) tag=TAG(hbox_kind,0); + else tag=TAG(vbox_kind,0); + tag |= hput_box_dimen(height(p),depth(p),width(p)); + tag |= hput_box_shift(shift_amount(p)); + tag |= hput_box_glue_set((glue_sign(p)==stretching)?+1:-1,glue_set(p),glue_order(p)); + hout_list_node2(list_ptr(p)); + break; + @ + +\subsection{Adjustments} +@= + case adjust_node: + hout_list_node2(adjust_ptr(p)); + tag=TAG(adjust_kind,1); + break; +@ + + +\subsection{Insertions} +\TeX's insertions are mapped to \HINT\ streams. + +@= +case ins_node: +@@; +break; +@ + +Here we consider stream content and come back to stream +definitions in section~\secref{streamdef}. +In a \HINT\ stream content node the stream +parameters |floating_penalty|, |split_max_depth|, and +|split_top_skip| are optional. If omitted, the defaults from the stream definition are +used. This is probably also for \TeX\ the most common situation. +It is, however, possible to supply more than one page template with different defaults +and while not very common, \TeX\ might change the parameters at any time. +Because we dont know which is the current page template, +it is not posible to compare the current parameter values against the defaults, +and we have to supply all the parameters always. +In a future version, we might have a \TeX\ primitive that allows us to +signal ``use the defaults''. + +@= +{ int k,n; + uint32_t pos; + list_t l; + info_t i=b000; + k=subtype(p); + n=hget_stream_no(k); + HPUT8(n); + link(temp_head)=null; + add_par_node(int_type,floating_penalty_code,float_cost(p)); + add_par_node(dimen_type,split_max_depth_code,depth(p)); + add_par_node(glue_type,split_top_skip_code,split_top_ptr(p)); + pos=hpos-hstart; + l.k=param_kind; + n=hout_param_list(link(temp_head),pos,&l); + flush_node_list(link(temp_head));@+ link(temp_head)=null; + if (n>=0) HPUT8(n); else i=b010; + hout_list_node2(ins_ptr(p)); + tag=TAG(stream_kind,i); +} +@ + + +\subsection{Marks} +We currently ignore Marks. + +@= + case mark_node: hpos--; return; +@ + +\subsection{Whatsit Nodes}\label{whatsit} +We have added custom whatsit nodes and now we switch based on the subtype. + +@= + case whatsit_node: + switch(subtype(p)) + { @@; + default: + MESSAGE("\nOutput of whatsit nodes subtype=%d not implemented\n", subtype(p)); + display_node(p); + MESSAGE("End of node"); + hpos--; + return; + } + break; +@ + +For \TeX's whatsit nodes that handle output files, no code is generated; +hence, we call |out_what| and simply remove the tag byte that is already +in the output. +When the \.{\\write} node arrives here, it is normaly handled +in |hlist_out| or |vlist_out| in an environment determined by +the output routine. For example \LaTeX\ redefines \.{\\protect} +as \.{\\noexpand} and these redefinitions need to be made +before calling |out_what| which expands the token list. +We should therefore add the definitions contained in the output routine +to mimic expanding inside an output routine. + + +@= + case open_node: + case write_node: + case close_node: out_what(p); + case special_node: hpos--; return; +@ + +\subsection{Paragraphs} +When we output a paragraph node, we have to consider a special case: +The parameter list is given by a reference number but the extended dimension +needs an |xdimen| node. In this case the reference number for the parameter +list comes first, while otherwise the extended dimension would come first. +To determine whether ther is a reference number for the parameter list, +the function |hout_param_list| is writing the parameter list to the output. +\noindent +@= +case graf_node: + { uint32_t pos, xpos, xsize; + list_t l; + pointer q; + int n,m; + info_t i=b000; + q=graf_extent(p); + n=hget_xdimen_no(q); + if (n>=0) HPUT8(n); + else + { xpos=hpos-hstart; hout_xdimen_node(p); xsize=(hpos-hstart)-xpos; i|=b100; } + pos=hpos-hstart; + l.k=param_kind; + m=hout_param_list(graf_params(p),pos,&l); + if (m>=0) + { if (i&b100) + { HPUTX(1); + memmove(hstart+xpos+1,hstart+xpos,xsize); + hpos++; + hstart[xpos]=m; + } + else + HPUT8(m); + } + else i|=b010; + pos=hpos-hstart; + l.k=list_kind; + hout_list_node(graf_list(p),pos,&l); + tag=TAG(par_kind,i); + } + break; +@ + +\subsection{Baseline Skips} +@= + case baseline_node: + { int n; + n= baseline_node_no(p); + if (n>0xFF) tag=hout_baselinespec(n); + else + { HPUT8(n); + tag=TAG(baseline_kind,b000); + } + } + break; + @ + +\subsection{Displayed Equations} +@= + case disp_node: + { uint32_t pos; + list_t l; + int n; + info_t i=b000; + pos=hpos-hstart; + l.k=param_kind; + n=hout_param_list(display_params(p),pos,&l); + if (n>=0) HPUT8(n); else i|=b100; + if (display_eqno(p)!=null && display_left(p)) + { hout_node(display_eqno(p)); i|=b010; } + pos=hpos-hstart; + l.k=list_kind; + hout_list_node(display_formula(p),pos,&l); + if (display_eqno(p)!=null && !display_left(p)) + { hout_node(display_eqno(p)); i|=b001; } + tag=TAG(math_kind,i); + /* the |display_no_bs(p)| tells whether the baseline skip is ignored */ + } + break; +@ +\subsection{Extended Boxes} +When we output an extended box, we have to consider a special case: the page templates. +Page templates are boxes that contain insertion points. These insertion points look +like regular insertions but with an empty content list. As a result the |hpack| and +|vpackage| routines might belive that they can compute the dimensions of the box +conntent when in fact they can not. + + +@= + case hset_node: + case vset_node: + { kind_t k= subtype(p)==hset_node?hset_kind:vset_kind; + info_t i=b000; + stretch_t s; + int n=set_extent(p); + i|=hput_box_dimen(height(p),depth(p),width(p)); + i|=hput_box_shift(shift_amount(p)); + s.f=set_stretch(p)/(double)ONE; s.o=set_stretch_order(p); + hput_stretch(&s); + s.f=set_shrink(p)/(double)ONE; s.o=set_shrink_order(p); + hput_stretch(&s); + if (hout_xdimen(n)) i|=b001; + hout_list_node2(list_ptr(p)); + tag=TAG(k,i); + } + break; +@ + +@= + case hpack_node: + case vpack_node: + { kind_t k= (subtype(p)==hpack_node?hpack_kind:vpack_kind); + info_t i=b000; + int n=pack_extent(p); + if (pack_m(p)==additional) i|=b001; + if (shift_amount(p)!=0) { HPUT32(shift_amount(p)); i|=b010; } + if (k==vpack_kind) HPUT32(pack_limit(p)); + if (hout_xdimen(n)) i|=b100; + hout_list_node2(list_ptr(p)); + tag=TAG(k,i); + } + break; +@ + +\subsection{Extended Alignments} +@= +case align_node: + { info_t i=b000; + if (align_m(p)==additional) i|=b001; + if (align_v(p)) i|=b010; + if (hout_xdimen(align_extent(p))) i|=b100; + hout_preamble(align_preamble(p)); + hout_align_list(align_list(p),align_v(p)); + tag=TAG(table_kind,i); + } +break; +@ + +In the preamble, we remove the unset nodes and retain only the list of tabskip glues. +@= +void hout_preamble(pointer p) +{ pointer q,r; + DBG(DBGBASIC,"Writing Preamble\n"); + q=p; + if (q!=null) r=link(q); else r=null; + while (r!=null) + { if (type(r)==unset_node) + { link(q)=link(r); + link(r)=null; flush_node_list(r); + } + else + q=r; + r=link(q); + } + hout_list_node2(p); + DBG(DBGBASIC,"End Preamble\n"); +} +@ + +In the |align_list| we have to convert the unset nodes back to box nodes or extended box nodes +packaged inside an item node. +When the viewer reads an item node, it will package the extended boxes to their natural size. +This is the size that is needed to compute the maximum width of a column. + +@= + +static void hout_item(pointer p, uint8_t t, uint8_t s) +{ info_t i=b000; + uint8_t n; + n=span_count(p)+1; + DBG(DBGBASIC,"Writing Item %d/%d->%d/%d\n",type(p),n,t,s); + display_node(p); + if (n==0) QUIT("Span count of item must be positive"); + if (n<7) i=n; else i=7; + HPUTTAG(item_kind,i); + if (i==7) HPUT8(n); + type(p)=t; subtype(p)=s; + hout_node(p); + HPUTTAG(item_kind,i); + DBG(DBGBASIC,"End Item\n"); +} + + +static void hout_item_list(pointer p, bool v) +{ list_t l; + uint32_t pos; + DBG(DBGBASIC,"Writing Item List\n"); + l.k=list_kind; + HPUTTAG(item_kind,b000); + pos=hpos-hstart; + HPUTX(2); + HPUT8(0); /* space for the list tag */ + HPUT8(0); /* space for the list size */ + l.p=hpos-hstart; + while(p> mem_min) + { if (is_char_node(p)) hout_node(p); + else if (type(p)==unset_node) hout_item(p,v?vlist_node:hlist_node,0); + else if (type(p)==unset_set_node) hout_item(p,whatsit_node,v?vset_node:hset_node); + else if (type(p)==unset_pack_node) hout_item(p,whatsit_node,v?vpack_node:hpack_node); + else hout_node(p); + p=link(p); + } + l.s=(hpos-hstart)-l.p; + hput_tags(pos,hput_list(pos+1,&l)); + HPUTTAG(item_kind,b000); + DBG(DBGBASIC,"End Item List\n"); +} + +void hout_align_list(pointer p, bool v) +{ list_t l; + uint32_t pos; + DBG(DBGBASIC,"Writing Align List\n"); + l.k=list_kind; + pos=hpos-hstart; + HPUTX(2); + HPUT8(0); /* space for the tag */ + HPUT8(0); /* space for the list size */ + l.p=pos+2; + while(p> mem_min) + { if (!is_char_node(p) && (type(p)==unset_node||type(p)==unset_set_node||type(p)==unset_pack_node)) + hout_item_list(list_ptr(p),v); + else + hout_node(p); + p=link(p); + } + l.s=(hpos-hstart)-l.p; + hput_tags(pos,hput_list(pos+1,&l)); + DBG(DBGBASIC,"End Align List\n"); +} +@ + +Inside the alignment list we will find various types of unset nodes, we convert them back +to regular nodes and put them inside an item node. + +@= +case unset_node: +case unset_set_node: +case unset_pack_node: + + +@ + +\subsection{Lists} +Two functions are provided here: |hout_list| will write a list given by the pointer |p| to the +output at the current position |hpos|. After the list has finished, it will move the list, if necessary, +and add the size information so that the final list will be at position |pos|; +|hout_list_node| uses |hout_list| but adds the tags to form a complete node. + + +@= + +static uint8_t hout_list(pointer p, uint32_t pos, list_t *l) +{ l->p=hpos-hstart; + while(p> mem_min) + { hout_node(p); + p=link(p); + } + l->s=(hpos-hstart)-l->p; + return hput_list(pos,l); +} + +static void hout_list_node(pointer p, uint32_t pos, list_t *l) +/* p is a pointer to a node list, + output the node list at position pos (thats where the tag goes) + using l->k as list kind, + and set l->p and l->s. +*/ +{ + hpos=hstart+pos; + HPUTX(3); + HPUT8(0); /* space for the tag */ + HPUT8(0); /* space for the list size */ + HPUT8(0); /* space for the size boundary byte */ + hput_tags(pos,hout_list(p,pos+1,l)); +} + + +static void hout_list_node2(pointer p) +{ list_t l; + uint32_t pos; + pos=hpos-hstart; + l.k=list_kind; + hout_list_node(p,pos,&l); +} +@ + +@= +static void hout_list_node(pointer p, uint32_t pos, list_t *l); +static void hout_list_node2(pointer p); +static uint8_t hout_list(pointer p, uint32_t pos, list_t *l); +@ + +\subsection{Parameter Lists} + +The next function is like |hout_list_node| but restricted to parameter nodes. + +@= + +static int hout_param_list(pointer p, uint32_t pos, list_t *l) +/* p is a pointer to a param node list, + either find a reference number to a predefined parameter list + and return the reference number or + output the node list at position pos (thats where the tag goes) + and set l->k, l->p and l->s, + and return -1; +*/ +{ int n; + hpos=hstart+pos; + if (p==null) + {HPUTX(2); hpos++;hput_tags(pos,TAG(param_kind,1)); l->s=0; return -1;} + HPUTX(3); + HPUT8(0); /* space for the tag */ + HPUT8(0); /* space for the list size */ + HPUT8(0); /* space for the size boundary byte*/ + l->p=hpos-hstart; + while(p> mem_min) + { hdef_param_node(par_type(p),par_number(p),par_value(p).i); + p=link(p); + } + l->s=(hpos-hstart)-l->p; + n=hget_param_list_no(l); + if (n>=0) + hpos=hstart+pos; + else + hput_tags(pos,hput_list(pos+1,l)); + return n; +} +@ + +@= +static int hout_param_list(pointer p, uint32_t pos, list_t *l); +@ + +\subsection{Labels, Links, and Outlines} +\indent +@= +case label_node: hpos--; new_label(p); return; +case start_link_node: +{ info_t i; + int n=new_start_link(p); + i=b010; + if (n>0xFF) { i|=b001; HPUT16(n);@+} @+else HPUT8(n); + tag= TAG(link_kind,i); +} +break; +case end_link_node: +{ info_t i; + int n=new_end_link(); + i=b000; + if (n>0xFF) { i|=b001; HPUT16(n);@+} @+else HPUT8(n); + tag= TAG(link_kind,i); +} +break; +case outline_node: hpos--; new_outline(p); return; +@ + +The routines to put labels and outlines into the definition section +are defined in {\tt hput.c}. + +\subsection{Images} +\indent +@= + case image_node: + { image_t i; + i.n=image_no(p); + i.w=image_width(p); + i.h=image_height(p); + i.p.f=image_stretch(p)/(double)ONE; + i.p.o=image_stretch_order(p); + i.m.f=image_shrink(p)/(double)ONE; + i.m.o=image_shrink_order(p); + tag=hput_image(&i); + } + break; +@ + + + +\subsection{Text} +The routines in this section are not yet ready. + +@= + +#if 0 +static void hchange_text_font(internal_font_number f) +{ uint8_t g; + if (f!=hfont) + { g=get_font_no(f); + if (g<8) + hputcc(FONT0_CHAR+g); + else + { hputcc(FONTN_CHAR); + hputcc(g); + } + hfont=f; + } +} + +static void hprint_text_char(pointer p) +{ uint8_t f,c; + f = font(p); + c = character(p); + hchange_text_font(f); + if (c<=SPACE_CHAR) hputcc(ESC_CHAR); + hputcc(c); +} + + +static void hprint_text_node(pointer p) +{ switch(type(p)) + { case hlist_node: + /* this used to be the |par_indent| case */ + goto nodex; + case glue_node: + if (subtype(p)>= cond_math_glue) goto nodex; + else + { pointer q=glue_ptr(p); + int i; + if (glue_equal(f_space_glue[hfont],q)) + { hputc(SPACE_CHAR); return; } + if (glue_equal(f_xspace_glue[hfont],q)) + { hputcc(XSPACE_CHAR); return; } + if (f_1_glue[hfont]==0 && (subtype(p)-1==space_skip_code)) + { pointer r=glue_par(subtype(p)-1); + add_glue_ref(r); + f_1_glue[hfont]=r; + } + if (f_1_glue[hfont]!=0 && glue_equal(f_1_glue[hfont],q)) + { hputcc(GLUE1_CHAR); return; } + if (f_2_glue[hfont]==0 && (subtype(p)-1==space_skip_code || subtype(p)-1==xspace_skip_code)) + { pointer r=glue_par(subtype(p)-1); + add_glue_ref(r); + f_2_glue[hfont]=r; + } + if (f_2_glue[hfont]!=0 && glue_equal(f_2_glue[hfont],q)) + { hputcc(GLUE2_CHAR); return; } + if (f_3_glue[hfont]==0) + { f_3_glue[hfont]=q; + add_glue_ref(q); + } + if (f_3_glue[hfont]!=0 && glue_equal(f_3_glue[hfont],q)) + { hputcc(GLUE3_CHAR); return; } + i = hget_glue_no(q); + if (i>=0) + { hputcc(GLUEN_CHAR); hputcc(i); return; } + } + break; + case ligature_node: + { int n; + pointer q; + for (n=0,q=lig_ptr(p); n<5 && q!=null; n++,q=link(q)) continue; + if (n==2) hputcc(LIG2_CHAR); + else if (n==3) hputcc(LIG3_CHAR); + else if (n==0) hputcc(LIG0_CHAR); + else goto nodex; + hprint_text_char(lig_char(p)); + for (q=lig_ptr(p);q!=null; q=link(q)) hprint_text_char(q); + return; + } + case disc_node: + if (post_break(p)==null && pre_break(p)!=null && replace_count(p)==0) + { pointer q; + q=pre_break(p); + if (is_char_node(q) && link(q)==null && font(q)==hfont && character(q)== hyphen_char[hfont]) + { if (is_auto_disc(p)) hputcc(DISC1_CHAR); + else hputcc(DISC2_CHAR); + return; + } + } + else if (post_break(p)==null && pre_break(p)==null && replace_count(p)==0 && !is_auto_disc(p)) + { hputcc(DISC3_CHAR); return; } + break; + case math_node: + if(width(p)!=0) goto nodex; + if(subtype(p)==before) hputcc(MATHON_CHAR); + else hputcc(MATHOFF_CHAR); + return; + default: + break; + } +nodex: + hout_node(p); +} + +static void hprint_text(pointer p) +{ internal_font_number f=hfont; + nesting++; + hprint_nesting(); + hprintf(" mem_min) + { if (is_char_node(p)) + hprint_text_char(p); + else + hprint_text_node(p); + p=link(p); + } + hchange_text_font(f); + hprintf(">\n"); + nesting--; +} +#endif + +@ + + +\section{\HINT\ Limitations} +\itemize +\item Kerns and glues using a width that depends on \.{\\hsize} or + \.{\\vsize} are not yet supported. +\item Tables where the width of a column depends on \.{\\hsize} or + \.{\\vsize} are not tested and probably not yet supported. +\item The encoding of horizontal lists as texts is not yet supported, + but it would make the \HINT\ file shorter and much better to read + when stretched into long \HINT\ format. +\enditemize + + +\appendix + +\section{Source Files} + +\subsection{Basic types} +To define basic types we use the file {\tt bastypes.h} from~\cite{MR:format}. + +\subsection{Hi\TeX\ routines: \tt hitex.c} +\indent +@p +#include +#include +#include "basetypes.h" +#include "error.h" +#include "hformat.h" +#include "hput.h" +#include "htex.h" +#include "hitex.h" + +/* from ktex.ch */ + +@@; +@@; +@@; + +@@; + +@@; + +@ + + +\subsection{Hi\TeX\ prototypes: \tt hitex.h} +@(hitex.h@>= +extern void hint_open(void); +extern void hint_close(void);@# + +extern pointer new_xdimen(scaled w, scaled h, scaled v); +extern void hget_image_information(pointer p); +extern pointer new_baseline_node(pointer bs, pointer ls, scaled lsl); +extern pointer new_set_node(void); +extern void hline_break(int final_widow_penalty); +extern pointer new_disp_node(void); +extern void add_par_node(eight_bits t, eight_bits n, int v); +extern pointer new_image_node(str_number n, char *a, char *e); +extern pointer new_setpage_node(eight_bits k, str_number n); +extern int hget_stream_no(int i); +extern pointer new_setstream_node(eight_bits n); +extern void hfinish_stream_group(void); +extern void hfinish_page_group(void); +extern void hfinish_stream_before_group(void); +extern void hfinish_stream_after_group(void); +extern void hfinish_outline_group(void); +extern void execute_output(pointer p); +extern void hint_debug_help(void); +@ + +\crosssections + +\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 hitex.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/hitexdir/htex.w b/Build/source/texk/web2c/hitexdir/htex.w new file mode 100644 index 00000000000..1115344785d --- /dev/null +++ b/Build/source/texk/web2c/hitexdir/htex.w @@ -0,0 +1,29180 @@ +% This file is part of HINT +% Copyright 2017-2021 Martin Ruckert +% +% 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. + +% The HINT program is derived from Donald E. Knuth's TeX and the +% subsequent e-TeX extension of TeX. + +% e-TeX is copyright (C) 1999-2012 by P. Breitenlohner (1994,98 by the NTS +% team); all rights are reserved. Copying of this file is authorized only if +% (1) you are P. Breitenlohner, or if (2) you make absolutely no changes to +% your copy. (Programs such as TIE allow the application of several change +% files to tex.web; the master files tex.web and etex.ch should stay intact.) + +% See etex_gen.tex for hints on how to install this program. +% And see etripman.tex for details about how to validate it. + +% This program is directly derived from Donald E. Knuth's TeX; +% the change history which follows and the reward offered for finders of +% bugs refer specifically to TeX; they should not be taken as referring +% to e-TeX, although the change history is relevant in that it +% demonstrates the evolutionary path followed. This program is not TeX; +% that name is reserved strictly for the program which is the creation +% and sole responsibility of Professor Knuth. + +% Version 0 was released in September 1982 after it passed a variety of tests. +% Version 1 was released in November 1983 after thorough testing. +% Version 1.1 fixed ``disappearing font identifiers'' et alia (July 1984). +% Version 1.2 allowed `0' in response to an error, et alia (October 1984). +% Version 1.3 made memory allocation more flexible and local (November 1984). +% Version 1.4 fixed accents right after line breaks, et alia (April 1985). +% Version 1.5 fixed \the\toks after other expansion in \edefs (August 1985). +% Version 2.0 (almost identical to 1.5) corresponds to "Volume B" (April 1986). +% Version 2.1 corrected anomalies in discretionary breaks (January 1987). +% Version 2.2 corrected "(Please type...)" with null \endlinechar (April 1987). +% Version 2.3 avoided incomplete page in premature termination (August 1987). +% Version 2.4 fixed \noaligned rules in indented displays (August 1987). +% Version 2.5 saved cur_order when expanding tokens (September 1987). +% Version 2.6 added 10sp slop when shipping leaders (November 1987). +% Version 2.7 improved rounding of negative-width characters (November 1987). +% Version 2.8 fixed weird bug if no \patterns are used (December 1987). +% Version 2.9 made \csname\endcsname's "relax" local (December 1987). +% Version 2.91 fixed \outer\def\a0{}\a\a bug (April 1988). +% Version 2.92 fixed \patterns, also file names with complex macros (May 1988). +% Version 2.93 fixed negative halving in allocator when mem_min<0 (June 1988). +% Version 2.94 kept open_log_file from calling fatal_error (November 1988). +% Version 2.95 solved that problem a better way (December 1988). +% Version 2.96 corrected bug in "Infinite shrinkage" recovery (January 1989). +% Version 2.97 corrected blunder in creating 2.95 (February 1989). +% Version 2.98 omitted save_for_after at outer level (March 1989). +% Version 2.99 caught $$\begingroup\halign..$$ (June 1989). +% Version 2.991 caught .5\ifdim.6... (June 1989). +% Version 2.992 introduced major changes for 8-bit extensions (September 1989). +% Version 2.993 fixed a save_stack synchronization bug et alia (December 1989). +% Version 3.0 fixed unusual displays; was more \output robust (March 1990). +% Version 3.1 fixed nullfont, disabled \write{\the\prevgraf} (September 1990). +% Version 3.14 fixed unprintable font names and corrected typos (March 1991). +% Version 3.141 more of same; reconstituted ligatures better (March 1992). +% Version 3.1415 preserved nonexplicit kerns, tidied up (February 1993). +% Version 3.14159 allowed fontmemsize to change; bulletproofing (March 1995). +% Version 3.141592 fixed \xleaders, glueset, weird alignments (December 2002). +% Version 3.1415926 was a general cleanup with minor fixes (February 2008). +% Version 3.14159265 was similar (January 2014). + +% A reward of $327.68 will be paid to the first finder of any remaining bug. + +% A preliminary version of TeX--XeT was released in April 1992. +% TeX--XeT version 1.0 was released in June 1992, +% version 1.1 prevented arith overflow in glue computation (Oct 1992). +% A preliminary e-TeX version 0.95 was operational in March 1994. +% Version 1.0beta was released in May 1995. +% Version 1.01beta fixed bugs in just_copy and every_eof (December 1995). +% Version 1.02beta allowed 256 mark classes (March 1996). +% Version 1.1 changed \group{type,level} -> \currentgroup{type,level}, +% first public release (October 1996). +% Version 2.0 development was started in March 1997; +% fixed a ligature-\beginR bug in January 1998; +% was released in March 1998. +% Version 2.1 fixed a \marks bug (when min_halfword<>0) (January 1999). +% Version 2.2 development was started in Feb 2003; released in Oct 2004. +% fixed a bug in sparse array handling (0=>null), Jun 2002; +% fixed a bug in \lastnodetype (cur_val=>cur_val_level) +% reported by Hartmut Henkel , +% fix by Fabrice Popineau , +% Jan 2004; +% another bug in sparse array handling (cur_ptr=>cur_chr) +% reported by Taco Hoekwater , Jul 2004; +% fixed a sparse array reference count bug (\let,\futurelet), +% fix by Bernd Raichle , Aug 2004; +% reorganized handling of banner, additional token list and +% integer parameters, and similar in order to reduce the +% interference between eTeX, pdfTeX, and web2c change files. +% adapted to tex.web 3.141592, revised glue rounding for mixed +% direction typesetting; +% fixed a bug in the revised glue rounding code, detected by +% Tigran Aivazian , Oct 2004. +% Version 2.3 development was started in Feb 2008; released in Apr 2011. +% fixed a bug in hyph_code handling (\savinghyphcodes) +% reported by Vladimir Volovich , Feb 2008. +% fixed the error messages for improper use of \protected, +% reported by Heiko Oberdiek +% , May 2010. +% some rearrangements to reduce interferences between +% e-TeX and pTeX, in part suggested by Hironori Kitagawa +% , Mar 2011. +% Version 2.4 fixed an uninitialized line number bug, released in May 2012. +% Version 2.5 development was started in Aug 2012; released in Feb 2013. +% better tracing of font definitions, reported by +% Bruno Le Floch , Jul 2012. +% Version 2.6 development was started in Mar 2013; released in ??? 201?. +% enable hyphenation of text between \beginL and \endL or +% between \beginR and \endR, problem reported by +% Vafa Khalighi , Nov 2013. +% better handling of right-to-left text -- to be done. + +% Although considerable effort has been expended to make the e-TeX program +% correct and reliable, no warranty is implied; the author disclaims any +% obligation or liability for damages, including but not limited to +% special, indirect, or consequential damages arising out of or in +% connection with the use or performance of this software. This work has +% been a ``labor of love'' and the author hopes that users enjoy it. + +% Here is TeX material that gets inserted after \input webmac +\def\hang{\hangindent 3em\noindent\ignorespaces} +\def\hangg#1 {\hang\hbox{#1 }} +\def\textindent#1{\hangindent2.5em\noindent\hbox to2.5em{\hss#1 }\ignorespaces} +\font\ninerm=cmr9 +\let\mc=\ninerm % medium caps for names like SAIL +\def\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\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{k\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 "" +\def\gglob{20, 26} % this should be the next two sections of "" + +\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 + #1\kern.05em}} +\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\dotdot{\mathrel{.\,.}} % double dot, used only in math mode +@s dotdot TeX +@* Introduction. +This is \eTeX, a program derived from and extending the capabilities of +\TeX, a document compiler intended to produce typesetting of high +quality. +The \PASCAL\ program that follows is the definition of \TeX82, a standard +@:PASCAL}{\PASCAL@> +@!@:TeX82}{\TeX82@> +version of \TeX\ that is designed to be highly portable so that identical output +will be obtainable on a great variety of computers. + +The main purpose of the following program is to explain the algorithms of \TeX\ +as clearly as possible. As a result, the program will not necessarily be very +efficient when a particular \PASCAL\ compiler has translated it into a +particular machine language. However, the program has been written so that it +can be tuned to run efficiently in a wide variety of operating environments +by making comparatively few changes. Such flexibility is possible because +the documentation that follows is written in the \.{WEB} language, which is +at a higher level than \PASCAL; the preprocessing step that converts \.{WEB} +to \PASCAL\ is able to introduce most of the necessary refinements. +Semi-automatic translation to other languages is also feasible, because the +program below does not make extensive use of features that are peculiar to +\PASCAL. + +A large piece of software like \TeX\ has inherent complexity that cannot +be reduced below a certain level of difficulty, although each individual +part is fairly simple by itself. The \.{WEB} language is intended to make +the algorithms as readable as possible, by reflecting the way the +individual program pieces fit together and by providing the +cross-references that connect different parts. Detailed comments about +what is going on, and about why things were done in certain ways, have +been liberally sprinkled throughout the program. These comments explain +features of the implementation, but they rarely attempt to explain the +\TeX\ language itself, since the reader is supposed to be familiar with +{\sl The \TeX book}. +@.WEB@> +@:TeXbook}{\sl The \TeX book@> + +@ The present implementation has a long ancestry, beginning in the summer +of~1977, when Michael~F. Plass and Frank~M. Liang designed and coded +a prototype +@^Plass, Michael Frederick@> +@^Liang, Franklin Mark@> +@^Knuth, Donald Ervin@> +based on some specifications that the author had made in May of that year. +This original proto\TeX\ included macro definitions and elementary +manipulations on boxes and glue, but it did not have line-breaking, +page-breaking, mathematical formulas, alignment routines, error recovery, +or the present semantic nest; furthermore, +it used character lists instead of token lists, so that a control sequence +like \.{\\halign} was represented by a list of seven characters. A +complete version of \TeX\ was designed and coded by the author in late +1977 and early 1978; that program, like its prototype, was written in the +{\mc SAIL} language, for which an excellent debugging system was +available. Preliminary plans to convert the {\mc SAIL} code into a form +somewhat like the present ``web'' were developed by Luis Trabb~Pardo and +@^Trabb Pardo, Luis Isidoro@> +the author at the beginning of 1979, and a complete implementation was +created by Ignacio~A. Zabala in 1979 and 1980. The \TeX82 program, which +@^Zabala Salelles, Ignacio Andr\'es@> +was written by the author during the latter part of 1981 and the early +part of 1982, also incorporates ideas from the 1979 implementation of +@^Guibas, Leonidas Ioannis@> +@^Sedgewick, Robert@> +@^Wyatt, Douglas Kirk@> +\TeX\ in {\mc MESA} that was written by Leonidas Guibas, Robert Sedgewick, +and Douglas Wyatt at the Xerox Palo Alto Research Center. Several hundred +refinements were introduced into \TeX82 based on the experiences gained with +the original implementations, so that essentially every part of the system +has been substantially improved. After the appearance of ``Version 0'' in +September 1982, this program benefited greatly from the comments of +many other people, notably David~R. Fuchs and Howard~W. Trickey. +A final revision in September 1989 extended the input character set to +eight-bit codes and introduced the ability to hyphenate words from +different languages, based on some ideas of Michael~J. Ferguson. +@^Fuchs, David Raymond@> +@^Trickey, Howard Wellington@> +@^Ferguson, Michael John@> + +No doubt there still is plenty of room for improvement, but the author +is firmly committed to keeping \TeX82 ``frozen'' from now on; stability +and reliability are to be its main virtues. + +On the other hand, the \.{WEB} description can be extended without changing +the core of \TeX82 itself, and the program has been designed so that such +extensions are not extremely difficult to make. +The |banner| string defined here should be changed whenever \TeX\ +undergoes any modifications, so that it will be clear which version of +\TeX\ might be the guilty party when a problem arises. +@^extensions to \TeX@> +@^system dependencies@> + +This program contains code for various features extending \TeX, +therefore this program is called `\eTeX' 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 ``\.{e-TRIP} test'' is available for +helping to determine whether a particular implementation deserves to be +known as `\eTeX'. + +@d eTeX_version 2 /* \.{\\eTeXversion} */ +@d eTeX_revision ".6" /* \.{\\eTeXrevision} */ +@d eTeX_version_string "-2.6" /*current \eTeX\ version*/ +@# +@d eTeX_banner "This is e-TeX, Version 3.14159265" " (HINT) " eTeX_version_string + /*printed when \eTeX\ starts*/ +@# +@d TeX_banner "This is TeX, Version 3.14159265 (HINT)" /*printed when \TeX\ starts*/ +@# +@d banner eTeX_banner +@# +@d TEX ETEX /*change program name into |ETEX|*/ +@# +@d eTeX_states 1 /*number of \eTeX\ state variables in |eqtb|*/ + +@ Different \PASCAL s have slightly different conventions, and the present +@!@:PASCAL H}{\ph@> +program expresses \TeX\ in terms of the \PASCAL\ that was +available to the author in 1982. Constructions that apply to +this particular compiler, which we shall call \ph, should help the +reader see how to make an appropriate interface for other systems +if necessary. (\ph\ is Charles Hedrick's modification of a compiler +@^Hedrick, Charles Locke@> +for the DECsystem-10 that was originally developed at the University of +Hamburg; cf.\ {\sl SOFTWARE---Practice \AM\ Experience \bf6} (1976), +29--42. The \TeX\ program below is intended to be adaptable, without +extensive changes, to most other versions of \PASCAL, so it does not fully +use the admirable features of \ph. Indeed, a conscious effort has been +made here to avoid using several idiosyncratic features of standard +\PASCAL\ itself, so that most of the code can be translated mechanically +into other high-level languages. For example, the `\&{with}' and `\\{new}' +features are not used, nor are pointer types, set types, or enumerated +scalar types; there are no `\&{var}' parameters, except in the case of files +--- \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 \.{WEB}. +@.WEB@> +For example, the portion of the program called `\X\glob:Global +variables\X' below will be replaced by a sequence of variable declarations +that starts in $\section\glob$ of this documentation. In this way, we are able +to define each individual global variable when we are prepared to +understand what it means; we do not have to define all of the globals at +once. Cross references in $\section\glob$, where it says ``See also +sections \gglob, \dots,'' also make it possible to look at the set of +all global variables, if desired. Similar remarks apply to the other +portions of the program. + +@p @
@; +#include "htex.h" +#include "hitex.h" +@@; +@# + +static void initialize(void) /*this procedure gets things started properly*/ + {@+@@; + @; + } @# +@@; +@@; + +@ 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/, but occasionally +it is necessary to insert header files and declare functions +very early in the program. For example, the function |s_no| +which uses |make_string| to convert a \CEE/ string into +a string number is used in |initialize| and needs a forward +declaration. + +@
= +extern int s_no(const char *str); + +@ Some of the code below is intended to be used only when diagnosing the +strange behavior that sometimes occurs when \TeX\ is being installed or +when system wizards are fooling around with \TeX\ without quite knowing +what they are doing. Such code will not normally be compiled; it is +delimited by the codewords `$|debug|\ldots|debug|$', with apologies +to people who wish to preserve the purity of English. + +Similarly, there is some conditional code delimited by +`$|stat|\ldots|tats|$' that is intended for use when statistics are to be +kept about \TeX's memory usage. The |stat| $\ldots$ |tats| code also +implements diagnostic information for \.{\\tracingparagraphs} and +\.{\\tracingpages}. +@^debugging@> + +@ 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 +|#ifdef| |INIT|\dots\ |#endif|. + +\TeX\ Live has established the common practice +to select the initialization code at runtime +using the |iniversion| variable. + +@= +@@/ +#ifdef @!INIT +if (iniversion) {@+@@;@+} +#endif + +@ Most of the external declarations needed are included using standard +header files. + +@s uint8_t int +@s int16_t int +@s uint16_t int +@s int32_t int +@s uint32_t int +@s halfword int +@s in TeX +@s line normal +@s to do + +@
= +#include +#include +#include +#include +#include +#include + +@ 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@> + +@= +@!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*/ +@!empty_string=256 /*the empty string follows after 256 characters*/ + +@ Like the preceding parameters, the following quantities can be changed +at compile time to extend or reduce \TeX's capacity. But if they are changed, +it is necessary to rerun the initialization program \.{INITEX} +@.INITEX@> +to generate new tables for the production \TeX\ program. +One can't simply make helter-skelter changes to the following constants, +since certain rather complex initialization +numbers are computed from them. They are defined here using +\.{WEB} macros, instead of being put into \PASCAL's |const| list, in order to +emphasize this distinction. + +@d mem_bot 0 /*smallest index in the |mem| array dumped by \.{INITEX}; + must not be less than |mem_min|*/ +@d mem_top 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 15000 /*maximum number of control sequences; it should be at most + about |(mem_max-mem_min)/(double)10|*/ +@d hash_prime 8501 /*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. + +@= +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.) + +@= +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. + +@= +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|*/ + +@= +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. + +@= +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@> + +@= +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@> + +@= +for (i=0; i<=037; i++) xchr[i]=chr(i); /* k\TeX\ */ +for (i=0177; i<=0377; i++) xchr[i]=chr(i); /* k\TeX\ */ + +@ 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. + +@= +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. + +@= +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@> + +@= +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*/ +int @!name_length;@/ /*this many characters are actually + relevant in |name_of_file| (the rest are blank)*/ + +@ k\TeX\ uses the {\tt kpathsearch} library to implement access to files. +To do so we declare \TeX's functions here and postpone the +actual implementations. These functions do not to 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 procedures return |false|. +This allows \TeX\ to undertake appropriate corrective action. +@^system dependencies@> + +@p static bool a_open_in(alpha_file *f); /*open a text file for input*/ +static bool a_open_out(alpha_file *f); /*open a text file for output*/ +static bool b_open_in(byte_file *f); /*open a binary file for input*/ +static bool b_open_out(byte_file *f); /*open a binary file for output*/ +static bool w_open_in(word_file *f); /*open a word file for input*/ +#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. + +@= +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) + @; + } + 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@> + +@= +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.) + +@= +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 program 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; /* k\TeX\ */ +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|*/ + +@= +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*/ + +@ @= +packed_ASCII_code @!str_pool[pool_size+1]; /*the characters*/ +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 strn_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; +} + +static bool str_eq_str(str_number @!s, char *@!t) + /*test equality of strings*/ +{@+int j, @!k; /*running indices*/ +if (length(s)!=(int)strlen(t)) return false; +j=str_start[s];k=0; +while (j < str_start[s+1]) + if (str_pool[j++]!=t[k++]) return false; +return true; +} + +@ 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 void 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; +@; +@; +} + +@ @d app_lc_hex(A) l=A; + if (l < 10) append_char(l+'0')@;@+else append_char(l-10+'a') + +@= +for (k=0; k<=255; k++) + {@+if ((@)) + {@+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 81 printable characters are needed. +@:TeXbook}{\sl The \TeX book@> +@^character set dependencies@> +@^system dependencies@> + +@= + (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. +@= +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 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(); +} + +@* 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*/ + +@= +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*/ + +@ @= +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@> + +@= +#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)@] +#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|. + +@= +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|. + +@= +void print_char(ASCII_code @!s) /*prints a single character*/ +{@+ +if (@) + 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@> + +@= +void print(char *s) /* the simple version */ +{ 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 ((@)) + 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: + +@= +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 the banner +and format identifier together will occupy at most |max_print_line| +character positions. + +k\TeX, according to the conventions of \TeX\ Live, + prints the |dump_name| if no format identifier is known. +@= +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. + +@= +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). + +@= +static void printn_esc(str_number @!s) /*prints escape character, then |s|*/ +{@+int c; /*the escape character code*/ +@; +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*/ +@; +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|. + +@= +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. + +@= +void print_int(int @!n) /*prints an integer in decimal form*/ +{@+int k; /*index to current digit; we assume that $|n|<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@> + +@= +void print_err(char *s) +{@+if (interaction==error_stop_mode) wake_up_terminal; + if (filelineerrorstylep) print_file_line(); /* k\TeX\ */ + 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*/ + +@= +int @!interaction; /*current level of interaction*/ + +@ @=interaction=error_stop_mode; + +@ \TeX\ is careful not to call |error| when the print |selector| setting +might be unusual. The only possible values of |selector| at the time of +error messages are + +\yskip\hang|no_print| (when |interaction==batch_mode| + and |log_file| not yet open); + +\hang|term_only| (when |interaction > batch_mode| and |log_file| not yet open); + +\hang|log_only| (when |interaction==batch_mode| and |log_file| is open); + +\hang|term_and_log| (when |interaction > batch_mode| and |log_file| is open). + +@= +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*/ + +@= +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. + +@= +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. + +@= +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);@/ +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*/ + +@= +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?*/ + +@ @= +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. + +@= +static void jump_out(void) +{@+ close_files_and_terminate(); exit(0); +} + +@ Here now is the general |error| routine. + +@= +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) @; +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(); + } +@; +} + +@ @= +loop@+{@+resume: clear_for_error_prompt();prompt_input("? "); +@.?\relax@> + if (last==first) return; + c=buffer[first]; + if (c >= 'a') c=c+'A'-'a'; /*convert to uppercase*/ + @; + } + +@ 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@> + +@= +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) + @@;@+break; +@t\4\4@>@; +#ifdef @!DEBUG +case 'D': {@+debug_help();goto resume;@+} +#endif +case 'E': if (base_ptr > 0) + {@+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': @@; +case 'I': @@; +case 'Q': case 'R': case 'S': @@; +case 'X': {@+interaction=scroll_mode;jump_out(); + } @+break; +default:do_nothing; +} @/ +@@; + +@ @= +{@+print("Type to proceed, S to scroll future error messages,");@/ +@.Type to proceed...@> +print_nl("R to run without stopping, Q to run quietly,");@/ +print_nl("I to insert something, "); +if (base_ptr > 0) 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@> + +@= +{@+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. + +@= +{@+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. + +@= +{@+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; +} + +@ @= +{@+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; +} + +@ @= +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*/ + } + +@= +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. + +@= +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@> + +@= +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(); + } + +@= +static int @!interrupt; /*should \TeX\ pause for instructions?*/ +static bool @!OK_to_interrupt; /*should interrupts be observed?*/ + +@ @= +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 some instructions for me (e.g.,`I\\showlists'),",@/ + "unless you just want to quit by typing `X'."); + deletions_allowed=false;error();deletions_allowed=true; + interrupt=0; + } +} + +@* 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*/ + +@= +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 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. + +@= +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 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 float(A) ((double)(A)) /*convert from |glue_ratio| to type |double|*/ +@d unfloat(A) ((glue_ratio)(A)) /*convert from |double| to type |glue_ratio|*/ +@d float_constant(A) ((double)(A)) /*convert |int| constant to |double|*/ + +@= +#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): + +@= +#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|*/ + +@= +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 { @;@/ + 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 { + 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*float(w.gr)));print_ln();@/ +@^real multiplication@> +print_int(w.hh.lh);print_char('=');print_int(w.hh.b0);print_char(':'); +print_int(w.hh.b1);print_char(';');print_int(w.hh.rh);print_char(' ');@/ +print_int(w.qqqq.b0);print_char(':');print_int(w.qqqq.b1);print_char(':'); +print_int(w.qqqq.b2);print_char(':');print_int(w.qqqq.b3); +} +#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*/ + +@= +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. + +@= +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*/ +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$ +|tats| is not ``commented out,'' \TeX\ will run a bit slower but it will +report these statistics when |tracing_stats| is sufficiently large. + +@= +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*/ + +@= +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 @@/ +@@; + +@ 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*/ + +@= +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 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@+{@; +@^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) + @; +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@> + +@= +{@+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. + +@= +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) @; +if (r==p) if (rlink(p)!=p) + @; +node_size(p)=q-p /*reset the size in case it grew*/ + +@ @= +{@+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. + +@= +{@+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 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) @; +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. + +@= +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 9 /*number of words to allocate for a box, set, or pack 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|, since that's the desired +|span_count| value if this |hlist_node| is changed to an |unset_node|. + +@p 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 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. +This field occupies a full word instead of a halfword, because +there's nothing to put in the other halfword; it is easier in \PASCAL\ to +use the full word than to risk leaving garbage in the unused half. + +@d mark_node 4 /*|type| of a mark node*/ +@d small_node_size 2 /*number of words to allocate for most node types*/ +@d mark_ptr(A) mem[A+1].i /*head of the token list for a mark*/ + +@ An |adjust_node|, which occurs only in horizontal lists, +specifies material that will be moved out into the surrounding +vertical list; i.e., it is used to implement \TeX's `\.{\\vadjust}' +operation. The |adjust_ptr| field points to the vlist containing this +material. + +@d adjust_node 5 /*|type| of an adjust node*/ +@d adjust_ptr(A) mark_ptr(A) /*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)&0x7F) /*how many subsequent nodes to replace*/ +@d set_replace_count(A,B) (subtype(A)=(B)&0x7F) +@d set_auto_disc(A) (subtype(A)|=0x80) +@d is_auto_disc(A) (subtype(A)&0x80) +@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 pointer new_disc(void) /*creates an empty |disc_node|*/ +{@+pointer p; /*the new node*/ +p=get_node(small_node_size);type(p)=disc_node; +set_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*/ + +@= +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 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 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=@@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 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(@); +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 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 unset_set_node 32 /*|type| for an unset |set_node|*/ +@d unset_pack_node 33 /*|type| for an unset |pack_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 setpage_head mem_top-14 /*head of page template list build by |new_setpage_node|*/ +@d max_page type(setpage_head) /* maximum page template number */ +@d max_stream subtype(setpage_head) /* maximum stream number */ +@d hi_mem_stat_min mem_top-14 /*smallest statically allocated word in + the one-word |mem|*/ +@d hi_mem_stat_usage 15 /*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. + +@= +int @!k; /*index into |mem|, |eqtb|, etc.*/ + +@ @= +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*/ +@; +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.) + +@= +#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 + +@ @= +#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*/ +@; +@; +@; +if (print_locs) @; +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 + +@ @= +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: + +@ @= +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: + +@ @= +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_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(')'); + } + } +@; +@; +@; +} +#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. + +@= +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 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_char(' ');font_in_short_display=font(p); + } + print_ASCII(qo(character(p))); + } + } + else@; + p=link(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: case unset_set_node: case unset_pack_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_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 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 @@; +@@; +static void print_xdimen(pointer p) +{ print_scaled(xdimen_width(p)); + if (xdimen_hfactor(p)!=0) + { print_char('+');print_scaled(xdimen_hfactor(p));print("*hsize");} + if (xdimen_vfactor(p)!=0) + { print_char('+');print_scaled(xdimen_vfactor(p));print("*vsize");} +} +extern void print_baseline_skip(int i); + +@ 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. + +@= +int @!depth_threshold; /*maximum nesting depth in box displays*/ +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 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); + } + +} + +void display_node(pointer p)@t~@> +{ +if (is_char_node(p)) print_font_and_char(p); +else switch (type(p)) { + case hlist_node: case vlist_node: case unset_node: case unset_set_node: case unset_pack_node: @@;@+break; + case rule_node: @@;@+break; + case ins_node: @@;@+break; + case whatsit_node: @@;@+break; + case glue_node: @@;@+break; + case kern_node: @@;@+break; + case math_node: @@;@+break; + case ligature_node: @@;@+break; + case penalty_node: @@;@+break; + case disc_node: @@;@+break; + case mark_node: @@;@+break; + case adjust_node: @@;@+break; + @t\4@>@@; + default:print("Unknown node type!"); + } +} + +@ @= +{@+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_set_node) print("set"); +else if (type(p)==unset_pack_node) print("pack"); +else if (type(p)==unset_node) + @@; +else{@+@; + if (shift_amount(p)!=0) + {@+print(", shifted ");print_scaled(shift_amount(p)); + } + } +node_list_display(list_ptr(p)); /*recursive call*/ +} + +@ @= +{@+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@> + +@= +g=float(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@> + } + +@ @= +{@+print_esc("rule(");print_rule_dimen(height(p));print_char('+'); +print_rule_dimen(depth(p));print(")x");print_rule_dimen(width(p)); +} + +@ @= +{@+print_esc("insert");print_int(qo(subtype(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*/ +} + +@ @= +if (subtype(p) >= a_leaders) @@; +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"); + } + } + +@ @= +{@+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. + +@= +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"); + } + +@ @= +{@+print_esc("math"); +if (subtype(p)==before) print("on"); +else print("off"); +if (width(p)!=0) + {@+print(", surrounded ");print_scaled(width(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(')'); +} + +@ @= +{@+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. + +@= +{@+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*/ +} + +@ @= +{@+print_esc("mark");print_mark(mark_ptr(p)); +} + +@ @= +{@+print_esc("vadjust");node_list_display(adjust_ptr(p)); /*recursive call*/ +} + +@ The recursive machinery is started by calling |show_box|. +@^recursion@> + +@p void show_box(pointer @!p) +{@+@; +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 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 void delete_glue_ref(pointer @!p) /*|p| points to a glue specification*/ +fast_delete_glue_ref(p) +void delete_xdimen_ref(pointer @!p) /*|p| points to a xdimen specification*/ +{@+if (xdimen_ref_count(p)==null) free_node(p, xdimen_node_size); + else decr(xdimen_ref_count(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 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: case unset_set_node: case unset_pack_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: @@; + 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@>@@; + 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*/ +@d add_xdimen_ref(A) incr(xdimen_ref_count(A)) /*new reference to an xdimen*/ + +@ 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 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) + {@+@; + link(q)=r;q=r;p=link(p); + } +link(q)=null;q=link(h);free_avail(h); +return q; +} + +@ @= +words=1; /*this setting occurs in more branches than any other*/ +if (is_char_node(p)) r=get_avail(); +else@; +while (words > 0) + {@+decr(words);mem[r+words]=mem[p+words]; + } + +@ @= +switch (type(p)) { +case hlist_node: case vlist_node: case unset_node: case unset_set_node: case unset_pack_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: @@;@+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 \ list in alignment template*/ +@d spacer 10 /*characters equivalent to blank space ( \.{\ } )*/ +@d letter 11 /*characters regarded as letters ( \.{A..Z}, \.{a..z} )*/ +@d other_char 12 /*none of the special character types*/ +@d active_char 13 /*characters that invoke macros ( \.{\char`\~} )*/ +@d par_end 13 /*end of paragraph ( \.{\\par} )*/ +@d match 13 /*match a macro parameter*/ +@d comment 14 /*characters that introduce comments ( \.\% )*/ +@d end_match 14 /*end of parameters to macro*/ +@d stop 14 /*end of job ( \.{\\end}, \.{\\dump} )*/ +@d invalid_char 15 /*characters that shouldn't appear ( \.{\^\^?} )*/ +@d delim_num 15 /*specify delimiter numerically ( \.{\\delimiter} )*/ +@d max_char_code 15 /*largest catcode for individual characters*/ + +@ Next are the ordinary run-of-the-mill command codes. Codes that are +|min_internal| or more represent internal quantities that might be +expanded by `\.{\\the}'. + +@d char_num 16 /*character specified numerically ( \.{\\char} )*/ +@d math_char_num 17 /*explicit math code ( \.{\\mathchar} )*/ +@d mark 18 /*mark definition ( \.{\\mark} )*/ +@d xray 19 /*peek inside of \TeX\ ( \.{\\show}, \.{\\showbox}, etc.~)*/ +@d make_box 20 /*make a box ( \.{\\box}, \.{\\copy}, \.{\\hbox}, etc.~)*/ +@d hmove 21 /*horizontal motion ( \.{\\moveleft}, \.{\\moveright} )*/ +@d vmove 22 /*vertical motion ( \.{\\raise}, \.{\\lower} )*/ +@d un_hbox 23 /*unglue a box ( \.{\\unhbox}, \.{\\unhcopy} )*/ +@d un_vbox 24 /*unglue a box ( \.{\\unvbox}, \.{\\unvcopy} )*/ +@d remove_item 25 /*nullify last item ( \.{\\unpenalty}, + \.{\\unkern}, \.{\\unskip} )*/ +@d hskip 26 /*horizontal glue ( \.{\\hskip}, \.{\\hfil}, etc.~)*/ +@d vskip 27 /*vertical glue ( \.{\\vskip}, \.{\\vfil}, etc.~)*/ +@d mskip 28 /*math glue ( \.{\\mskip} )*/ +@d kern 29 /*fixed space ( \.{\\kern})*/ +@d mkern 30 /*math kern ( \.{\\mkern} )*/ +@d leader_ship 31 /*use a box ( \.{\\shipout}, \.{\\leaders}, etc.~)*/ +@d halign 32 /*horizontal table alignment ( \.{\\halign} )*/ +@d valign 33 /*vertical table alignment ( \.{\\valign} )*/ +@d no_align 34 /*temporary escape from alignment ( \.{\\noalign} )*/ +@d vrule 35 /*vertical rule ( \.{\\vrule} )*/ +@d hrule 36 /*horizontal rule ( \.{\\hrule} )*/ +@d insert 37 /*vlist inserted in box ( \.{\\insert} )*/ +@d vadjust 38 /*vlist inserted in enclosing paragraph ( \.{\\vadjust} )*/ +@d ignore_spaces 39 /*gobble |spacer| tokens ( \.{\\ignorespaces} )*/ +@d after_assignment 40 /*save till assignment is done ( \.{\\afterassignment} )*/ +@d after_group 41 /*save till group is done ( \.{\\aftergroup} )*/ +@d break_penalty 42 /*additional badness ( \.{\\penalty} )*/ +@d start_par 43 /*begin paragraph ( \.{\\indent}, \.{\\noindent} )*/ +@d ital_corr 44 /*italic correction ( \.{\\/} )*/ +@d accent 45 /*attach accent in text ( \.{\\accent} )*/ +@d math_accent 46 /*attach accent in math ( \.{\\mathaccent} )*/ +@d discretionary 47 /*discretionary texts ( \.{\\-}, \.{\\discretionary} )*/ +@d eq_no 48 /*equation number ( \.{\\eqno}, \.{\\leqno} )*/ +@d left_right 49 /*variable delimiter ( \.{\\left}, \.{\\right} )*/ + /*( 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 in the |ship_out| routine. + +Numeric values are assigned to |vmode|, |hmode|, and |mmode| so that +\TeX's ``big semantic switch'' can select the appropriate thing to +do by computing the value |abs(mode)+cur_cmd|, where |mode| is the current +mode and |cur_cmd| is the current command code. + +@d vmode 1 /*vertical mode*/ +@d hmode (vmode+max_command+1) /*horizontal mode*/ +@d mmode (hmode+max_command+1) /*math mode*/ + +@p 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 (-1000*unity) /*|prev_depth| value that is ignored*/ +@d unknown_depth (-2000*unity) /*|prev_depth| value that is unknown*/ + +@= +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*/ + +@= +list_state_record @!nest[nest_size+1]; +int @!nest_ptr; /*first unused location of |nest|*/ +static int @!max_nest_stack; /*maximum of |nest_ptr| when pushing*/ +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. + +@= +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; +@; + +@ 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 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 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) + {@+@; + if (link(contrib_head)!=null) + print_nl("### recent contributions:"); + } + show_box(link(nest[p].head_field)); + @; + } +} + +@ @= +switch (abs(m)/(max_command+1)) { +case 0: {@+print_nl("prevdepth "); + if (a.sc <= ignore_depth) + { if (a.sc <= unknown_depth) print("unknown"); else 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 be 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_null_font (frozen_control_sequence+10) + /*permanent `\.{\\nullfont}'*/ +@d font_id_base (frozen_null_font-font_base) + /*begins table of 257 permanent font identifiers*/ +@d undefined_control_sequence (frozen_null_font+257) /*dummy location*/ +@d glue_base (undefined_control_sequence+1) /*beginning of region 3*/ + +@= +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.) + +@= +{@+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) + +@=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. + +@= +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. + +@= +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@> + +@ @= +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}'. + +@= +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; + +@ @= +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 tex_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|*/ + +@= +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@> + +@ @= +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; + @/@@/ + 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''*/ + +@= +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; + } + +@ @= +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) @@; +else@@; + +@ @= +{@+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))|*/ +} + +@ @= +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 heldover after a split*/ +@d global_defs_code 43 /*override \.{\\global} specifications*/ +@d cur_fam_code 44 /*current family*/ +@d escape_char_code 45 /*escape character for token output*/ +@d default_hyphen_char_code 46 /*value of \.{\\hyphenchar} when a font is loaded*/ +@d default_skew_char_code 47 /*value of \.{\\skewchar} when a font is loaded*/ +@d end_line_char_code 48 /*character placed at the right end of the buffer*/ +@d new_line_char_code 49 /*character that prints as |print_ln|*/ +@d language_code 50 /*current hyphenation table*/ +@d left_hyphen_min_code 51 /*minimum left hyphenation fragment size*/ +@d right_hyphen_min_code 52 /*minimum right hyphenation fragment size*/ +@d holding_inserts_code 53 /*do not remove insertion nodes from \.{\\box255}*/ +@d error_context_lines_code 54 /*maximum intermediate line pairs shown*/ +@d tex_int_pars 55 /*total number of \TeX's integer parameters*/ +@# +@d etex_int_base tex_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 eTeX_state_code (etex_int_base+5) /*\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) + +@= +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; +@/@@/ +default:print("[unknown integer parameter!]"); +} +} + +@ The integer parameter names must be entered into the hash table. + +@= +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@> + +@ @= +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@> + +@= +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. +@^system dependencies@> +k\TeX\ calls |tl_now| to obtain the current time as a |tm| structure. +@p static void fix_date_and_time(void) +{@+struct tm *gmt=tl_now(); + time= gmt->tm_hour*60+gmt->tm_min; + day= gmt->tm_mday; + month=gmt->tm_mon+1; + year=gmt->tm_year+1900; +} + +@ @= +{@+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); +} + +@ @=c=escape_char + +@ @=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 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; + } +} +@# +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 another global variable, if the previous +routines are going to work. + +@= +static int @!old_setting; + +@ The final region of |eqtb| contains the dimension parameters defined +here, and the 256 \.{\\dimen} registers. + +@d par_indent_code 0 /*indentation of paragraphs*/ +@d math_surround_code 1 /*space around math in text*/ +@d line_skip_limit_code 2 /*threshold for |line_skip| instead of |baseline_skip|*/ +@d hsize_code 3 /*line width in horizontal mode*/ +@d vsize_code 4 /*page height in vertical mode*/ +@d max_depth_code 5 /*maximum depth of boxes on main pages*/ +@d split_max_depth_code 6 /*maximum depth of boxes on split pages*/ +@d box_max_depth_code 7 /*maximum depth of explicit vboxes*/ +@d hfuzz_code 8 /*tolerance for overfull hbox messages*/ +@d vfuzz_code 9 /*tolerance for overfull vbox messages*/ +@d delimiter_shortfall_code 10 /*maximum amount uncovered by variable delimiters*/ +@d null_delimiter_space_code 11 /*blank space in null delimiters*/ +@d script_space_code 12 /*extra space after subscript or superscript*/ +@d pre_display_size_code 13 /*length of text preceding a display*/ +@d display_width_code 14 /*length of line for displayed equation*/ +@d display_indent_code 15 /*indentation of line for displayed equation*/ +@d overfull_rule_code 16 /*width of rule that identifies overfull hboxes*/ +@d hang_indent_code 17 /*amount of hanging indentation*/ +@d h_offset_code 18 /*amount of horizontal offset when shipping pages out*/ +@d v_offset_code 19 /*amount of vertical offset when shipping pages out*/ +@d emergency_stretch_code 20 /*reduces badnesses on final pass of line-breaking*/ +@d dimen_pars 21 /*total number of dimension parameters*/ +@d scaled_base (dimen_base+dimen_pars) + /*table of 256 user-defined \.{\\dimen} registers*/ +@d eqtb_size (scaled_base+255) /*largest subscript of |eqtb|*/ +@# +@d dimen(A) eqtb[scaled_base+A].sc +@d dimen_par(A) eqtb[dimen_base+A].sc /*a scaled quantity*/ +@d dimen_hfactor(A) hfactor_eqtb[scaled_base+A].sc +@d dimen_vfactor(A) vfactor_eqtb[scaled_base+A].sc +@d dimen_par_hfactor(A) hfactor_eqtb[dimen_base+A].sc +@d dimen_par_vfactor(A) vfactor_eqtb[dimen_base+A].sc +@d par_indent dimen_par(par_indent_code) +@d math_surround dimen_par(math_surround_code) +@d line_skip_limit dimen_par(line_skip_limit_code) +@d hsize dimen_par(hsize_code) +@d vsize dimen_par(vsize_code) +@d max_depth dimen_par(max_depth_code) +@d split_max_depth dimen_par(split_max_depth_code) +@d box_max_depth dimen_par(box_max_depth_code) +@d hfuzz dimen_par(hfuzz_code) +@d vfuzz dimen_par(vfuzz_code) +@d delimiter_shortfall dimen_par(delimiter_shortfall_code) +@d null_delimiter_space dimen_par(null_delimiter_space_code) +@d script_space dimen_par(script_space_code) +@d pre_display_size dimen_par(pre_display_size_code) +@d display_width dimen_par(display_width_code) +@d display_indent dimen_par(display_indent_code) +@d overfull_rule dimen_par(overfull_rule_code) +@d hang_indent dimen_par(hang_indent_code) +@d h_offset dimen_par(h_offset_code) +@d v_offset dimen_par(v_offset_code) +@d emergency_stretch dimen_par(emergency_stretch_code) + +@p 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; +default:print("[unknown dimen parameter!]"); +} +} + +@ @= +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@> + +@ @= +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; + +@ @= +for (k=dimen_base; k<=eqtb_size; k++) hfactor_eqtb[k].sc=vfactor_eqtb[k].sc=eqtb[k].sc=0; + +@ @= +{@+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@>@@;@/ +#ifdef @!STAT +static void show_eqtb(pointer @!n) +{@+if (n < active_base) print_char('?'); /*this can't happen*/ +else if (n < glue_base) @@; +else if (n < local_base) @@; +else if (n < int_base) @@; +else if (n < dimen_base) @@; +else if (n <= eqtb_size) @@; +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|. + +@= +memory_word @!eqtb0[eqtb_size-active_base+1], *const @!eqtb = @!eqtb0-active_base; +memory_word hfactor_eqtb0[dimen_pars+256]={{{0}}}, *const @!hfactor_eqtb = @!hfactor_eqtb0-dimen_base; +memory_word vfactor_eqtb0[dimen_pars+256]={{{0}}}, *const @!vfactor_eqtb = @!vfactor_eqtb0-dimen_base; +scaled par_shape_hfactor=0, par_shape_vfactor=0; +scaled hhsize=0,hvsize=0; +static quarterword @!xeq_level0[eqtb_size-int_base+1], *const @!xeq_level = @!xeq_level0-int_base; + +@ @= +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. + +@= +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*/ + +@= +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*/ + +@ @= +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]; + +@ @= +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*/ +@; +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@; + goto found; + } + p=next(p); + } +found: return p; +} + +@ @= +{@+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@> + +@= +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. + +@= +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{@+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. + +@= +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*/ +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; +} +#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: + +@= +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. + +@= +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: if (chr_code==0) print_esc("expandafter") + @;@+break; +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");@+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") + @;@+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; + @@;@/ + } @+break; /*there are no other cases*/ +case the: if (chr_code==0) print_esc("the") + @;@+break; +case toks_register: @@;@+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: + +@= +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 current value of |eqtb[undefined_control_sequence]|. + +\yskip\hangg 3) If |save_type(p)==insert_token|, then |save_index(p)| +is a token that should be inserted into \TeX's input when the current +group ends. + +\yskip\hangg 4) If |save_type(p)==level_boundary|, then |save_level(p)| +is a code explaining what kind of group we were previously in, and +|save_index(p)| points to the level boundary word at the bottom of +the entries for that group. +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@>@@; + +@ 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 page_group 17 +@d stream_group 18 +@d stream_before_group 19 +@d stream_after_group 20 +@d outline_group 21 +@d max_group_code 21 + +@= +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|. + +@ @= +static memory_word @!save_stack[save_size+1]; +static memory_word @!save_hfactor[save_size+1]; +static memory_word @!save_vfactor[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|. + +@= +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 +@d saved_hfactor(A) save_hfactor[save_ptr+A].i +@d saved_vfactor(A) save_vfactor[save_ptr+A].i + +@p 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; +@/@@/ +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]; + if (p>=dimen_base) + { save_hfactor[save_ptr]=hfactor_eqtb[p]; + save_vfactor[save_ptr]=vfactor_eqtb[p]; + } + else if (p==par_shape_loc) + { save_hfactor[save_ptr].i=par_shape_hfactor; + save_vfactor[save_ptr].i=par_shape_vfactor; + } + 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; +if (p==par_shape_loc) +{ par_shape_hfactor=cur_hfactor; + par_shape_vfactor=cur_vfactor; +} +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 (cur_level==level_one)@t\1@> + { if (p==dimen_base+hsize_code) + { hhsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity); return; @+} + if (p==dimen_base+vsize_code) + { hvsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity); return; @+} + } + if (xeq_level[p]!=cur_level) + {@+eq_save(p, xeq_level[p]);xeq_level[p]=cur_level; + } +eqtb[p].i=w; +if (p>=dimen_base) +{ hfactor_eqtb[p].i=cur_hfactor; + vfactor_eqtb[p].i=cur_vfactor; +} +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")@;@/ +{@t\1@>xeq_level[p]=level_one; + if (p==dimen_base+hsize_code) + hhsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity); + else if (p==dimen_base+vsize_code) + hvsize=w+round(((double)cur_hfactor*hhsize +(double)cur_vfactor*hvsize)/unity); + else + { eqtb[p].i=w; + if (p>=dimen_base) + { hfactor_eqtb[p].i=cur_hfactor; + vfactor_eqtb[p].i=cur_vfactor; + } +} +} +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); +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); + @; + } +else confusion("curlevel"); /*|unsave| is not used when |cur_group==bottom_level|*/ +@:this can't happen curlevel}{\quad curlevel@> +} + +@ @= +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) + @@; + 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]; + @; + } + } +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. + +@= +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*/ + if (p==par_shape_loc) + { par_shape_hfactor=save_hfactor[save_ptr].i; + par_shape_vfactor=save_vfactor[save_ptr].i; + } +#ifdef @!STAT + if (tracing_restores > 0) restore_trace(p,"restoring"); +#endif +@;@/ + } +else if (xeq_level[p]!=level_one) + {@t\1@>@+eqtb[p]=save_stack[save_ptr]; + if (p>=dimen_base) + { hfactor_eqtb[p]=save_hfactor[save_ptr]; + vfactor_eqtb[p]=save_vfactor[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 +@;@/ + } + +@ @= +#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@> + +@= +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. + +@= +static int @!mag_set; /*if nonzero, this magnification should be used henceforth*/ + +@ @= +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$*/ + +@ @= +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 the final right brace of an output routine +is included at the end of its token list. + +Here is an example macro definition that illustrates these conventions. +After \TeX\ processes the text +$$\.{\\def\\mac a\#1\#2 \\b \{\#1\\-a \#\#1\#2 \#2\}}$$ +the definition of \.{\\mac} is represented as a token list containing +$$\def\,{\hskip2pt} +\vbox{\halign{\hfil#\hfil\cr +(reference count), |letter|\,\.a, |match|\,\#, |match|\,\#, |spacer|\,\.\ , +\.{\\b}, |end_match|,\cr +|out_param|\,1, \.{\\-}, |letter|\,\.a, |spacer|\,\.\ , |mac_param|\,\#, +|other_char|\,\.1,\cr +|out_param|\,2, |spacer|\,\.\ , |out_param|\,2.\cr}}$$ +The procedure |scan_toks| builds such token lists, and |macro_call| +does the parameter matching. +@^reference counts@> + +Examples such as +$$\.{\\def\\m\{\\def\\m\{a\}\ b\}}$$ +explain why reference counts would be needed even if \TeX\ had no \.{\\let} +operation: When the token list for \.{\\m} is being read, the redefinition of +\.{\\m} changes the |eqtb| entry before the token list has been fully +consumed, so we dare not simply destroy a token list when its +control sequence is being redefined. + +If the parameter-matching part of a definition ends with `\.{\#\{}', +the corresponding token list will have `\.\{' just before the `|end_match|' +and also at the very end. The first `\.\{' is used to delimit the parameter; the +second one keeps the first from disappearing. + +@ The procedure |show_token_list|, which prints a symbolic form of +the token list that starts at a given node |p|, illustrates these +conventions. The token list being displayed should not begin with a reference +count. However, the procedure is intended to be robust, so that if the +memory links are awry or if |p| is not really a pointer to a token list, +nothing catastrophic will happen. + +An additional parameter |q| is also given; this parameter is either null +or it points to a node in the token list where a certain magic computation +takes place that will be explained later. (Basically, |q| is non-null when +we are printing the two-line context information at the time of an error +message; |q| marks the place corresponding to where the second line +should begin.) + +For example, if |p| points to the node containing the first \.a in the +token list above, then |show_token_list| will print the string +$$\hbox{`\.{a\#1\#2\ \\b\ ->\#1\\-a\ \#\#1\#2\ \#2}';}$$ +and if |q| points to the node containing the second \.a, +the magic computation will be performed just before the second \.a is printed. + +The generation will stop, and `\.{\\ETC.}' will be printed, if the length +of printing exceeds a given limit~|l|. Anomalous entries are printed in the +form of control sequences that are not followed by a blank space, e.g., +`\.{\\BAD.}'; this cannot be confused with actual control sequences because +a real control sequence named \.{BAD} would come out `\.{\\BAD\ }'. + +@= +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) @; + @; + p=link(p); + } +if (p!=null) print_esc("ETC."); +@.ETC@> + +} + +@ @= +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@; + } + +@ 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. + +@= +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) + {@+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. + +@= +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); + } + +@= +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@>@@/ +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: + +@= +typedef struct { + quarterword @!state_field, @!index_field; + halfword @!start_field, @!loc_field, @!limit_field, @!name_field; + } in_state_record; + +@ @= +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*/ + +@ 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*/ + +@= +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; +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*/ + +@= +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: + +@= +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 \ part of an alignment +template is being scanned; + +\hang|v_template|, if the \ 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 \ template*/ +@d v_template 2 /*|token_type| code for \ 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. + +@= +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 +\ 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 \ template, after which it is set to zero; the +\ template begins when a tab mark or \.{\\cr} occurs at a time that +|align_state==0|. + +@= +static int @!align_state; /*group level with respect to current alignment*/ + +@ Thus, the ``current input state'' can be very complicated indeed; there +can be many levels and each level can arise in a variety of ways. The +|show_context| procedure, which is used by \TeX's error-reporting routine to +print out the current input state on all levels down to the most recent +line of characters from an input file, illustrates most of these conventions. +The global variable |base_ptr| contains the lowest level that was +displayed by this procedure. + +@= +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?*/ +@@/ +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)) + @@; + 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*/ +} + +@ @= +{@+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) + {@+@; + @; + } + else{@+@; + @; + } + selector=old_setting; /*stop pseudoprinting*/ + @; + 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@> + +@= +if (name <= 17) + if (terminal_input) + if (base_ptr==0) print_nl("<*>");else print_nl(" "); + else{@+print_nl(" + 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(' ') + +@ @= +switch (token_type) { +case parameter: print_nl(" ");@+break; +case u_template: case v_template: print_nl("