From 7fa7e2984ec7346b13ce8d3a84826552d30f00ea Mon Sep 17 00:00:00 2001 From: Luigi Scarso Date: Sat, 11 Mar 2017 00:31:06 +0000 Subject: sync with luatex r.6287 and mpost r.2125 git-svn-id: svn://tug.org/texlive/trunk@43454 c570f23f-e606-0410-a88d-b1316a301751 --- Build/source/texk/web2c/ac/web2c.ac | 4 +- Build/source/texk/web2c/luatexdir/am/libluatex.am | 30 +- Build/source/texk/web2c/luatexdir/am/luatex.am | 2 +- Build/source/texk/web2c/luatexdir/font/luafont.w | 4444 ++++++------ Build/source/texk/web2c/luatexdir/lang/texlang.w | 51 +- Build/source/texk/web2c/luatexdir/lua/helpers.w | 26 + .../texk/web2c/luatexdir/lua/lauxlib_bridge.h | 22 +- Build/source/texk/web2c/luatexdir/lua/lepdflib.cc | 5 - Build/source/texk/web2c/luatexdir/lua/limglib.c | 3 +- Build/source/texk/web2c/luatexdir/lua/liolibext.c | 891 +-- .../source/texk/web2c/luatexdir/lua/lnewtokenlib.c | 49 + Build/source/texk/web2c/luatexdir/lua/loslibext.c | 27 +- Build/source/texk/web2c/luatexdir/lua/lstatslib.c | 50 +- Build/source/texk/web2c/luatexdir/lua/lstrlibext.c | 249 +- Build/source/texk/web2c/luatexdir/lua/luainit.w | 2248 +++--- Build/source/texk/web2c/luatexdir/lua/luastuff.w | 1242 ++-- Build/source/texk/web2c/luatexdir/lua/luatex-api.h | 18 +- .../source/texk/web2c/luatexdir/lua/luatex-core.c | 253 + .../texk/web2c/luatexdir/lua/luatex-core.lua | 209 + Build/source/texk/web2c/luatexdir/lua/mplibstuff.w | 93 + Build/source/texk/web2c/luatexdir/luaffi/README | 2 +- Build/source/texk/web2c/luatexdir/luaffi/ffi.c | 2 +- Build/source/texk/web2c/luatexdir/luaffi/ffi.h | 7 + .../fontforge/fontforge/configure-pfaedit.h | 8 +- Build/source/texk/web2c/luatexdir/luatex.c | 4 +- Build/source/texk/web2c/luatexdir/ptexlib.h | 2 +- Build/source/texk/web2c/luatexdir/tex/commands.w | 1762 ++--- Build/source/texk/web2c/luatexdir/tex/dumpdata.w | 1052 +-- .../source/texk/web2c/luatexdir/tex/equivalents.h | 96 +- Build/source/texk/web2c/luatexdir/tex/linebreak.w | 6 +- .../source/texk/web2c/luatexdir/tex/maincontrol.w | 7278 +++++++++---------- Build/source/texk/web2c/luatexdir/tex/mlist.w | 64 +- .../texk/web2c/luatexdir/tex/postlinebreak.w | 2 +- Build/source/texk/web2c/luatexdir/tex/texmath.w | 5138 ++++++------- Build/source/texk/web2c/luatexdir/tex/texnodes.h | 14 +- Build/source/texk/web2c/luatexdir/tex/texnodes.w | 7552 ++++++++++---------- Build/source/texk/web2c/luatexdir/tex/textoken.h | 1 + Build/source/texk/web2c/luatexdir/tex/textoken.w | 141 + Build/source/texk/web2c/luatexdir/utils/utils.w | 117 + Build/source/texk/web2c/mplibdir/am/libmplib.am | 26 +- Build/source/texk/web2c/mplibdir/am/mplib.am | 9 +- Build/source/texk/web2c/mplibdir/mp.w | 35 +- Build/source/texk/web2c/mplibdir/mpmath.w | 2 +- Build/source/texk/web2c/mplibdir/mpmathdouble.w | 2 +- Build/source/texk/web2c/mplibdir/pngout.w | 3 + Build/source/texk/web2c/mplibdir/svgout.w | 1 + 46 files changed, 17123 insertions(+), 16119 deletions(-) create mode 100644 Build/source/texk/web2c/luatexdir/lua/helpers.w create mode 100644 Build/source/texk/web2c/luatexdir/lua/luatex-core.c create mode 100644 Build/source/texk/web2c/luatexdir/lua/luatex-core.lua create mode 100644 Build/source/texk/web2c/luatexdir/lua/mplibstuff.w (limited to 'Build') diff --git a/Build/source/texk/web2c/ac/web2c.ac b/Build/source/texk/web2c/ac/web2c.ac index bb9d559ed20..7acbb825187 100644 --- a/Build/source/texk/web2c/ac/web2c.ac +++ b/Build/source/texk/web2c/ac/web2c.ac @@ -35,8 +35,8 @@ m4_define([kpse_tex_progs], [dnl [[euptex], [yes], [yes], [e-upTeX], [ptexenc]], [[aleph], [yes], [], [Aleph], []], [[pdftex], [yes], [yes], [pdfTeX], [xpdf libpng]], -[[luatex], [yes], [], [LuaTeX], [poppler mpfr cairo libpng zziplib lua52]], -[[luajittex], [yes], [], [LuaJITTeX], [poppler mpfr cairo libpng zziplib luajit]], +[[luatex], [yes], [], [LuaTeX], [poppler mpfr libpng zziplib lua52]], +[[luajittex], [yes], [], [LuaJITTeX], [poppler mpfr libpng zziplib luajit]], [[mp], [yes], [], [MetaPost], [mpfr cairo libpng]], [[pmp], [yes], [], [pMetaPost], [mpfr cairo libpng ptexenc]], [[upmp], [yes], [], [upMetaPost], [mpfr cairo libpng ptexenc]], diff --git a/Build/source/texk/web2c/luatexdir/am/libluatex.am b/Build/source/texk/web2c/luatexdir/am/libluatex.am index b512cffcc11..cabbaa0b5d6 100644 --- a/Build/source/texk/web2c/luatexdir/am/libluatex.am +++ b/Build/source/texk/web2c/luatexdir/am/libluatex.am @@ -34,19 +34,21 @@ libluajittex_a_CXXFLAGS = $(WARNING_CXXFLAGS) dist_libluatex_a_SOURCES = \ luatexdir/lua/lstrlibext.c nodist_libluatex_a_SOURCES = \ - luastuff.c texluac.c \ + helpers.c luastuff.c texluac.c \ $(dist_libluatex_sources) \ $(nodist_libluatex_sources) dist_libluajittex_a_SOURCES = \ luatexdir/lua/lauxlib_bridge.h \ - luatexdir/lua/lstrlibextjit.c + luatexdir/lua/lstrlibext.c nodist_libluajittex_a_SOURCES = \ - luajitstuff.c texluajitc.c \ + luastuff.c texluajitc.c \ $(dist_libluatex_sources) \ $(nodist_libluatex_sources) -$(libluatex_a_OBJECTS): libff.a libmplib.a libluamisc.a $(POPPLER_DEPEND) -$(libluajittex_a_OBJECTS): libff.a libmplib.a libluajitmisc.a $(POPPLER_DEPEND) +## mplib "stub" backends are in mplibstuff.c +$(libluatex_a_OBJECTS): libff.a libmplibcore.a libluamisc.a $(POPPLER_DEPEND) +$(libluajittex_a_OBJECTS): libff.a libmplibcore.a libluajitmisc.a $(POPPLER_DEPEND) + ## from luatexdir ## @@ -214,27 +216,34 @@ dist_libluatex_sources += \ ## luatex_lua_ctangle = $(ctangle_silent)CWEBINPUTS=$(srcdir)/luatexdir/lua $(ctangle) +helpers.c: ctangle$(EXEEXT) luatexdir/lua/helpers.w + $(luatex_lua_ctangle) helpers.w luainit.c: ctangle$(EXEEXT) luatexdir/lua/luainit.w $(luatex_lua_ctangle) luainit.w -luajitstuff.c: ctangle$(EXEEXT) luatexdir/lua/luajitstuff.w - $(luatex_lua_ctangle) luajitstuff.w +#luajitstuff.c: ctangle$(EXEEXT) luatexdir/lua/luajitstuff.w +# $(luatex_lua_ctangle) luajitstuff.w luanode.c: ctangle$(EXEEXT) luatexdir/lua/luanode.w $(luatex_lua_ctangle) luanode.w luastuff.c: ctangle$(EXEEXT) luatexdir/lua/luastuff.w $(luatex_lua_ctangle) luastuff.w luatoken.c: ctangle$(EXEEXT) luatexdir/lua/luatoken.w $(luatex_lua_ctangle) luatoken.w +mplibstuff.c: ctangle$(EXEEXT) luatexdir/lua/mplibstuff.w + $(luatex_lua_ctangle) mplibstuff.w texluac.c: ctangle$(EXEEXT) luatexdir/lua/texluac.w $(luatex_lua_ctangle) texluac.w texluajitc.c: ctangle$(EXEEXT) luatexdir/lua/texluajitc.w $(luatex_lua_ctangle) texluajitc.w -libluatex_web += luatexdir/lua/luainit.w luatexdir/lua/luajitstuff.w +libluatex_web += luatexdir/lua/helpers.w +#libluatex_web += luatexdir/lua/luainit.w luatexdir/lua/luajitstuff.w +libluatex_web += luatexdir/lua/luainit.w libluatex_web += luatexdir/lua/luanode.w luatexdir/lua/luastuff.w luatexdir/lua/luatoken.w +libluatex_web += luatexdir/lua/mplibstuff.w libluatex_web += luatexdir/lua/texluac.w luatexdir/lua/texluajitc.w nodist_libluatex_sources += luainit.c luanode.c luatoken.c - +nodist_libluatex_sources += mplibstuff.c dist_libluatex_sources += \ @@ -255,7 +264,8 @@ dist_libluatex_sources += \ luatexdir/lua/ltexiolib.c \ luatexdir/lua/ltexlib.c \ luatexdir/lua/lnewtokenlib.c \ - luatexdir/lua/luatex-api.h + luatexdir/lua/luatex-api.h \ + luatexdir/lua/luatex-core.c ## from luatexdir/pdf ## diff --git a/Build/source/texk/web2c/luatexdir/am/luatex.am b/Build/source/texk/web2c/luatexdir/am/luatex.am index d67ae11f3f8..2436d8891d4 100644 --- a/Build/source/texk/web2c/luatexdir/am/luatex.am +++ b/Build/source/texk/web2c/luatexdir/am/luatex.am @@ -48,7 +48,7 @@ luajittex_CXXFLAGS = $(WARNING_CXXFLAGS) luatex_LDFLAGS = -export-dynamic luajittex_LDFLAGS = -export-dynamic $(LUAJIT_LDEXTRA) -luatex_postldadd = libmplib.a $(MPFR_LIBS) $(GMP_LIBS) $(CAIRO_LIBS) $(PIXMAN_LIBS) +luatex_postldadd = libmplibcore.a $(MPFR_LIBS) $(GMP_LIBS) luatex_postldadd += $(ZZIPLIB_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) $(POPPLER_LIBS) luatex_postldadd += $(LDADD) libmputil.a libunilib.a libmd5.a $(lua_socketlibs) diff --git a/Build/source/texk/web2c/luatexdir/font/luafont.w b/Build/source/texk/web2c/luatexdir/font/luafont.w index 6ea9a4d5eea..9c7354a3f8f 100644 --- a/Build/source/texk/web2c/luatexdir/font/luafont.w +++ b/Build/source/texk/web2c/luatexdir/font/luafont.w @@ -1,2222 +1,2222 @@ -% luafont.w -% -% Copyright 2006-2011 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c -#include "ptexlib.h" -#include "lua/luatex-api.h" - -#define noVERBOSE - -/* todo: also keys */ - -const char *font_type_strings[] = { - "unknown", "virtual", "real", NULL -}; - -const char *font_writingmode_strings[] = { - "unknown", "horizontal", "vertical", NULL -}; - -const char *font_identity_strings[] = { - "unknown", "horizontal", "vertical", NULL -}; - -const char *font_format_strings[] = { - "unknown", "type1", "type3", "truetype", "opentype", NULL -}; - -const char *font_embedding_strings[] = { - "unknown", "no", "subset", "full", NULL -}; - -const char *ligature_type_strings[] = { - "=:", "=:|", "|=:", "|=:|", "", "=:|>", "|=:>", "|=:|>", "", "", "", "|=:|>>", NULL -}; - -const char *MATH_param_names[] = { - "nil", - "ScriptPercentScaleDown", - "ScriptScriptPercentScaleDown", - "DelimitedSubFormulaMinHeight", - "DisplayOperatorMinHeight", - "MathLeading", - "AxisHeight", - "AccentBaseHeight", - "FlattenedAccentBaseHeight", - "SubscriptShiftDown", - "SubscriptTopMax", - "SubscriptBaselineDropMin", - "SuperscriptShiftUp", - "SuperscriptShiftUpCramped", - "SuperscriptBottomMin", - "SuperscriptBaselineDropMax", - "SubSuperscriptGapMin", - "SuperscriptBottomMaxWithSubscript", - "SpaceAfterScript", - "UpperLimitGapMin", - "UpperLimitBaselineRiseMin", - "LowerLimitGapMin", - "LowerLimitBaselineDropMin", - "StackTopShiftUp", - "StackTopDisplayStyleShiftUp", - "StackBottomShiftDown", - "StackBottomDisplayStyleShiftDown", - "StackGapMin", - "StackDisplayStyleGapMin", - "StretchStackTopShiftUp", - "StretchStackBottomShiftDown", - "StretchStackGapAboveMin", - "StretchStackGapBelowMin", - "FractionNumeratorShiftUp", - "FractionNumeratorDisplayStyleShiftUp", - "FractionDenominatorShiftDown", - "FractionDenominatorDisplayStyleShiftDown", - "FractionNumeratorGapMin", - "FractionNumeratorDisplayStyleGapMin", - "FractionRuleThickness", - "FractionDenominatorGapMin", - "FractionDenominatorDisplayStyleGapMin", - "SkewedFractionHorizontalGap", - "SkewedFractionVerticalGap", - "OverbarVerticalGap", - "OverbarRuleThickness", - "OverbarExtraAscender", - "UnderbarVerticalGap", - "UnderbarRuleThickness", - "UnderbarExtraDescender", - "RadicalVerticalGap", - "RadicalDisplayStyleVerticalGap", - "RadicalRuleThickness", - "RadicalExtraAscender", - "RadicalKernBeforeDegree", - "RadicalKernAfterDegree", - "RadicalDegreeBottomRaisePercent", - "MinConnectorOverlap", - "SubscriptShiftDownWithSuperscript", - "FractionDelimiterSize", - "FractionDelimiterDisplayStyleSize", - "NoLimitSubFactor", - "NoLimitSupFactor", - NULL, -}; - -/* here for now, may be useful elsewhere */ - -int ff_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]); - -int ff_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { - const char *name = (def) ? luaL_optstring(L, narg, def) : luaL_checkstring(L, narg); - int i; - for (i=0; lst[i]; i++) - if (strcmp(lst[i], name) == 0) - return i; - return -1; -} - -#define dump_intfield(L,n,c) \ - lua_push_string_by_name(L,n); \ - lua_pushinteger(L, c); \ - lua_rawset(L, -3); \ - -#define dump_stringfield(L,n,c) \ - lua_push_string_by_name(L,n); \ - lua_pushstring(L, c); \ - lua_rawset(L, -3); - -#define dump_booleanfield(L,n,c) \ - lua_push_string_by_name(L,n); \ - lua_pushboolean(L, c); \ - lua_rawset(L, -3); - -static void dump_math_kerns(lua_State * L, charinfo * co, int l, int id) -{ - int i; - for (i = 0; i < l; i++) { - lua_newtable(L); - if (id==top_left_kern) { - dump_intfield(L, height, co->top_left_math_kern_array[(2*i)]); - dump_intfield(L, kern, co->top_left_math_kern_array[(2*i)+1]); - } else if (id==top_right_kern) { - dump_intfield(L, height, co->top_right_math_kern_array[(2*i)]); - dump_intfield(L, kern, co->top_right_math_kern_array[(2*i)+1]); - } else if (id==bottom_right_kern) { - dump_intfield(L, height, co->bottom_right_math_kern_array[(2*i)]); - dump_intfield(L, kern, co->bottom_right_math_kern_array[(2*i)+1]); - } else if (id==bottom_left_kern) { - dump_intfield(L, height, co->bottom_left_math_kern_array[(2*i)]); - dump_intfield(L, kern, co->bottom_left_math_kern_array[(2*i)+1]); - } - lua_rawseti(L, -2, (i + 1)); - } -} - -static void font_char_to_lua(lua_State * L, internal_font_number f, charinfo * co) -{ - liginfo *l; - kerninfo *ki; - - lua_createtable(L, 0, 10); - - dump_intfield(L,width,get_charinfo_width(co)); - dump_intfield(L,height,get_charinfo_height(co)); - dump_intfield(L,depth,get_charinfo_depth(co)); - - if (get_charinfo_italic(co) != 0) { - dump_intfield(L,italic,get_charinfo_italic(co)); - } - if (get_charinfo_vert_italic(co) != 0) { - dump_intfield(L,vert_italic,get_charinfo_vert_italic(co)); - } - if (get_charinfo_top_accent(co) !=0 && get_charinfo_top_accent(co) != INT_MIN) { - dump_intfield(L,top_accent,get_charinfo_top_accent(co)); - } - if (get_charinfo_bot_accent(co) != 0 && get_charinfo_bot_accent(co) != INT_MIN) { - dump_intfield(L,bot_accent,get_charinfo_bot_accent(co)); - } - if (get_charinfo_ef(co) != 1000) { - dump_intfield(L,expansion_factor,get_charinfo_ef(co)); - } - if (get_charinfo_lp(co) != 0) { - dump_intfield(L,left_protruding,get_charinfo_lp(co)); - } - if (get_charinfo_rp(co) != 0) { - dump_intfield(L,right_protruding,get_charinfo_rp(co)); - } - if (font_encodingbytes(f) == 2) { - dump_intfield(L,index,get_charinfo_index(co)); - } - if (get_charinfo_name(co) != NULL) { - dump_stringfield(L,name,get_charinfo_name(co)); - } - if (get_charinfo_tounicode(co) != NULL) { - dump_stringfield(L,tounicode,get_charinfo_tounicode(co)); - } - if (get_charinfo_tag(co) == list_tag) { - dump_intfield(L,next,get_charinfo_remainder(co)); - } - if (get_charinfo_used(co)) { - dump_booleanfield(L,used,(get_charinfo_used(co) ? true : false)); - } - if (get_charinfo_tag(co) == ext_tag) { - extinfo *h; - h = get_charinfo_hor_variants(co); - if (h != NULL) { - int i = 1; - lua_push_string_by_name(L,horiz_variants); - lua_newtable(L); - while (h != NULL) { - lua_createtable(L, 0, 5); - dump_intfield(L, glyph, h->glyph); - dump_intfield(L, extender, h->extender); - dump_intfield(L, start, h->start_overlap); - dump_intfield(L, end, h->end_overlap); - dump_intfield(L, advance, h->advance); - lua_rawseti(L, -2, i); - i++; - h = h->next; - } - lua_rawset(L, -3); - } - h = get_charinfo_vert_variants(co); - if (h != NULL) { - int i = 1; - lua_push_string_by_name(L,vert_variants); - lua_newtable(L); - while (h != NULL) { - lua_createtable(L, 0, 5); - dump_intfield(L, glyph, h->glyph); - dump_intfield(L, extender, h->extender); - dump_intfield(L, start, h->start_overlap); - dump_intfield(L, end, h->end_overlap); - dump_intfield(L, advance, h->advance); - lua_rawseti(L, -2, i); - i++; - h = h->next; - } - lua_rawset(L, -3); - } - } - ki = get_charinfo_kerns(co); - if (ki != NULL) { - int i; - lua_push_string_by_name(L,kerns); - lua_createtable(L, 10, 1); - for (i = 0; !kern_end(ki[i]); i++) { - if (kern_disabled(ki[i])) { - /* skip like in lookup */ - } else { - lua_rawgeti(L, -1, kern_char(ki[i])); - if (lua_type(L,-1) == LUA_TNIL) { - lua_pop(L,1); - if (kern_char(ki[i]) == right_boundarychar) { - lua_push_string_by_name(L,right_boundary); - } else { - lua_pushinteger(L, kern_char(ki[i])); - } - lua_pushinteger(L, kern_kern(ki[i])); - lua_rawset(L, -3); - } else { - /* first one wins */ - lua_pop(L,1); - } - } - } - lua_rawset(L, -3); - } - l = get_charinfo_ligatures(co); - if (l != NULL) { - int i; - lua_push_string_by_name(L,ligatures); - lua_createtable(L, 10, 1); - for (i = 0; !lig_end(l[i]); i++) { - if (lig_char(l[i]) == right_boundarychar) { - lua_push_string_by_name(L,right_boundary); - } else { - lua_pushinteger(L, lig_char(l[i])); - } - lua_createtable(L, 0, 2); - lua_push_string_by_name(L,type); - lua_pushinteger(L, lig_type(l[i])); - lua_rawset(L, -3); - lua_push_string_by_name(L,char); - lua_pushinteger(L, lig_replacement(l[i])); - lua_rawset(L, -3); - lua_rawset(L, -3); - } - lua_rawset(L, -3); - } - - lua_push_string_by_name(L,mathkern); - lua_newtable(L); - { - int i, j; - i = get_charinfo_math_kerns(co, top_right_kern); - j = 0; - if (i > 0) { - j++; - lua_push_string_by_name(L,top_right); - lua_newtable(L); - dump_math_kerns(L, co, i, top_right_kern); - lua_rawset(L, -3); - } - i = get_charinfo_math_kerns(co, top_left_kern); - if (i > 0) { - j++; - lua_push_string_by_name(L,top_left); - lua_newtable(L); - dump_math_kerns(L, co, i, top_left_kern); - lua_rawset(L, -3); - } - i = get_charinfo_math_kerns(co, bottom_right_kern); - if (i > 0) { - j++; - lua_push_string_by_name(L,bottom_right); - lua_newtable(L); - dump_math_kerns(L, co, i, bottom_right_kern); - lua_rawset(L, -3); - } - i = get_charinfo_math_kerns(co, bottom_left_kern); - if (i > 0) { - j++; - lua_push_string_by_name(L,bottom_left); - lua_newtable(L); - dump_math_kerns(L, co, i, bottom_left_kern); - lua_rawset(L, -3); - } - if (j > 0) - lua_rawset(L, -3); - else - lua_pop(L, 2); - } -} - -static void write_lua_parameters(lua_State * L, int f) -{ - int k; - lua_push_string_by_name(L,parameters); - lua_newtable(L); - for (k = 1; k <= font_params(f); k++) { - switch (k) { - case slant_code: - dump_intfield(L,slant,font_param(f, k)); - break; - case space_code: - dump_intfield(L,space,font_param(f, k)); - break; - case space_stretch_code: - dump_intfield(L,space_stretch,font_param(f, k)); - break; - case space_shrink_code: - dump_intfield(L,space_shrink,font_param(f, k)); - break; - case x_height_code: - dump_intfield(L,x_height,font_param(f, k)); - break; - case quad_code: - dump_intfield(L,quad,font_param(f, k)); - break; - case extra_space_code: - dump_intfield(L,extra_space,font_param(f, k)); - break; - default: - lua_pushinteger(L, font_param(f, k)); - lua_rawseti(L, -2, k); - } - } - lua_rawset(L, -3); -} - -@ @c -static void write_lua_math_parameters(lua_State * L, int f) -{ - int k; - lua_push_string_by_name(L,MathConstants); - lua_newtable(L); - for (k = 1; k <= font_math_params(f); k++) { - lua_pushinteger(L, font_math_param(f, k)); - if (k <= MATH_param_max) { - lua_setfield(L, -2, MATH_param_names[k]); - } else { - lua_rawseti(L, -2, k); - } - } - lua_rawset(L, -3); -} - -int font_to_lua(lua_State * L, int f) -{ - int k; - charinfo *co; - if (font_cache_id(f) > 0) { - /* fetch the table from the registry if it was - saved there by |font_from_lua()| */ - lua_rawgeti(L, LUA_REGISTRYINDEX, font_cache_id(f)); - /* fontdimens can be changed from tex code */ - write_lua_parameters(L, f); - return 1; - } - - lua_newtable(L); - lua_push_string_by_name(L,name); - lua_pushstring(L, font_name(f)); - lua_rawset(L, -3); - if (font_area(f) != NULL) { - dump_stringfield(L,area,font_area(f)); - } - if (font_filename(f) != NULL) { - dump_stringfield(L,filename,font_filename(f)); - } - if (font_fullname(f) != NULL) { - dump_stringfield(L,fullname,font_fullname(f)); - } - if (font_psname(f) != NULL) { - dump_stringfield(L,psname,font_psname(f)); - } - if (font_encodingname(f) != NULL) { - dump_stringfield(L,encodingname,font_encodingname(f)); - } - - dump_booleanfield(L,used,(font_used(f) ? true : false)); - dump_stringfield(L,type,font_type_strings[font_type(f)]); - dump_stringfield(L,format,font_format_strings[font_format(f)]); - dump_stringfield(L,writingmode,font_writingmode_strings[font_writingmode(f)]); - dump_stringfield(L,identity,font_identity_strings[font_identity(f)]); - dump_stringfield(L,embedding,font_embedding_strings[font_embedding(f)]); - - dump_intfield(L,units_per_em,font_units_per_em(f)); - dump_intfield(L,size,font_size(f)); - dump_intfield(L,designsize,font_dsize(f)); - dump_intfield(L,checksum,font_checksum(f)); - dump_intfield(L,slant,font_slant(f)); - dump_intfield(L,extend,font_extend(f)); - dump_intfield(L,direction,font_natural_dir(f)); - dump_intfield(L,encodingbytes,font_encodingbytes(f)); - dump_booleanfield(L,oldmath,font_oldmath(f)); - dump_intfield(L,tounicode,font_tounicode(f)); - - /* the next one is read only */ - if (font_max_shrink(f) != 0) { - dump_intfield(L,shrink,font_max_shrink(f)); - } - if (font_max_stretch(f) != 0) { - dump_intfield(L,stretch,font_max_stretch(f)); - } - if (font_step(f) != 0) { - dump_intfield(L,step,font_step(f)); - } - if (font_auto_expand(f) != 0) { - dump_booleanfield(L,auto_expand,font_auto_expand(f)); - } - if (pdf_font_attr(f) != 0) { - char *s = makecstring(pdf_font_attr(f)); - dump_stringfield(L,attributes,s); - free(s); - } - - /* params */ - write_lua_parameters(L, f); - write_lua_math_parameters(L, f); - - /* chars */ - lua_push_string_by_name(L,characters); - lua_createtable(L, font_tables[f]->charinfo_size, 0); /* all characters */ - if (has_left_boundary(f)) { - co = get_charinfo(f, left_boundarychar); - lua_push_string_by_name(L,left_boundary); - font_char_to_lua(L, f, co); - lua_rawset(L, -3); - } - if (has_right_boundary(f)) { - co = get_charinfo(f, right_boundarychar); - lua_push_string_by_name(L,right_boundary); - font_char_to_lua(L, f, co); - lua_rawset(L, -3); - } - for (k = font_bc(f); k <= font_ec(f); k++) { - if (quick_char_exists(f, k)) { - lua_pushinteger(L, k); - co = get_charinfo(f, k); - font_char_to_lua(L, f, co); - lua_rawset(L, -3); - } - } - lua_rawset(L, -3); - - if (font_cache_id(f) == 0) { /* renew */ - int r; - lua_pushvalue(L, -1); - r = luaL_ref(L, LUA_REGISTRYINDEX); /* pops the table */ - set_font_cache_id(f, r); - } - return 1; -} - -#define count_hash_items(L,name,n) \ - n = 0; \ - lua_key_rawgeti(name); \ - if (lua_type(L, -1) == LUA_TTABLE) { \ - lua_pushnil(L); \ - while (lua_next(L, -2) != 0) { \ - n++; \ - lua_pop(L, 1); \ - } \ - } \ - if (n) { \ - /* keep table on stack */ \ - } else{ \ - lua_pop(L, 1); \ - } - -@ @c -#define streq(a,b) (strcmp(a,b)==0) - -#define append_packet(k) { *(cp++) = (eight_bits) (k); } - -#define do_store_four(l) { \ - append_packet((l & 0xFF000000) >> 24); \ - append_packet((l & 0x00FF0000) >> 16); \ - append_packet((l & 0x0000FF00) >> 8); \ - append_packet((l & 0x000000FF)); \ -} - -@ @c -static void append_float(eight_bits ** cpp, float a) -{ - unsigned int i; - eight_bits *cp = *cpp; - union U { - float a; - eight_bits b[sizeof(float)]; - } u; - u.a = a; - for (i = 0; i < sizeof(float); i++) - append_packet(u.b[i]); - *cpp = cp; -} - -static int n_enum_field(lua_State * L, int name_index, int dflt, const char **values) -{ - int k, t; - const char *s; - int i = dflt; - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - t = lua_type(L,-1); - if (t == LUA_TNUMBER) { - i = (int) lua_tointeger(L, -1); - } else if (t == LUA_TSTRING) { - s = lua_tostring(L, -1); - k = 0; - while (values[k] != NULL) { - if (strcmp(values[k], s) == 0) { - i = k; - break; - } - k++; - } - } - lua_pop(L, 1); - return i; -} - -static int n_boolean_field(lua_State * L, int name_index, int dflt) -{ - int i = dflt; - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - if (lua_isboolean(L, -1)) { - i = lua_toboolean(L, -1); - } - lua_pop(L, 1); - return i; -} - -static char *n_string_field_copy(lua_State * L, int name_index, const char *dflt) -{ - char *i; - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - if (lua_type(L,-1) == LUA_TSTRING) { - i = xstrdup(lua_tostring(L, -1)); - } else if (dflt == NULL) { - i = NULL; - } else { - i = xstrdup(dflt); - } - lua_pop(L, 1); - return i; -} - -static const char *n_string_field(lua_State * L, int name_index) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - return lua_tostring(L,-1); -} - -static int n_some_field(lua_State * L, int name_index) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - return lua_type(L,-1); -} - -/*static void init_font_string_pointers(lua_State * L){}*/ - -static int count_char_packet_bytes(lua_State * L) -{ - register int i; - register int l = 0; - int ff = 0; - for (i = 1; i <= (int) lua_rawlen(L, -1); i++) { - lua_rawgeti(L, -1, i); - if (lua_istable(L, -1)) { - lua_rawgeti(L, -1, 1); - if (lua_type(L,-1) == LUA_TSTRING) { - const char *s = lua_tostring(L, -1); - if (lua_key_eq(s, font)) { - l += 5; - ff = 1; - } else if (lua_key_eq(s, char)) { - if (ff == 0) { - l += 5; - } - l += 5; - ff = 1; - } else if (lua_key_eq(s, slot)) { - l += 10; - ff = 1; - } else if (lua_key_eq(s, comment) || lua_key_eq(s, nop)) { - ; - } else if (lua_key_eq(s, push) || lua_key_eq(s, pop)) { - l++; - } else if (lua_key_eq(s, rule)) { - l += 9; - } else if (lua_key_eq(s, right) || lua_key_eq(s, node) - || lua_key_eq(s, down) || lua_key_eq(s, image)) { - l += 5; - } else if (lua_key_eq(s, scale)) { - l += sizeof(float) + 1; - } else if (lua_key_eq(s, special) || lua_key_eq(s, lua)) { - size_t len; - lua_rawgeti(L, -2, 2); - if (lua_type(L,-1) == LUA_TSTRING) { - (void) lua_tolstring(L, -1, &len); - lua_pop(L, 1); - if (len > 0) { - l = (int) (l + 5 + (int) len); - } - } else { - lua_pop(L, 1); - normal_error("vf command","invalid packet special"); - /* fprintf(stdout, "invalid packet special!\n"); */ - } - } else { - normal_error("vf command","unknown packet command"); - /* fprintf(stdout, "unknown packet command %s!\n", s); */ - } - } else { - normal_error("vf command","no packet command"); - /* fprintf(stdout, "no packet command!\n"); */ - } - lua_pop(L, 1); /* command name */ - } - lua_pop(L, 1); /* item */ - } - return l; -} - -static scaled sp_to_dvi(halfword sp, halfword atsize) -{ - double result, mult; - mult = (double) (atsize / 65536.0); - result = (double) (sp * 16.0); - return floor(result / mult); -} - -@ @c -static void read_char_packets(lua_State * L, int *l_fonts, charinfo * co, internal_font_number f, int atsize) -{ - int i, n, m; - size_t l; - int cmd; - const char *s; - eight_bits *cpackets, *cp; - int ff = 0; - int max_f = 0; - int pc = count_char_packet_bytes(L); - if (pc <= 0) - return; - while (l_fonts[(max_f + 1)] != 0) - max_f++; - cp = cpackets = xmalloc((unsigned) (pc + 1)); - for (i = 1; i <= (int) lua_rawlen(L, -1); i++) { - lua_rawgeti(L, -1, i); - if (lua_istable(L, -1)) { - /* fetch the command code */ - lua_rawgeti(L, -1, 1); - if (lua_type(L,-1) == LUA_TSTRING) { - s = lua_tostring(L, -1); - cmd = 0; - if (lua_key_eq(s, font)) { - cmd = packet_font_code; - } else if (lua_key_eq(s, char)) { - cmd = packet_char_code; - if (ff == 0) { - append_packet(packet_font_code); - ff = l_fonts[1]; - do_store_four(ff); - } - } else if (lua_key_eq(s, slot)) { - cmd = packet_nop_code; - lua_rawgeti(L, -2, 2); - n = (int) luaL_checkinteger(L, -1); - if (n ==0) { - ff = f; - } else { - ff = (n > max_f ? l_fonts[1] : l_fonts[n]); - } - lua_rawgeti(L, -3, 3); - n = (int) luaL_checkinteger(L, -1); - lua_pop(L, 2); - append_packet(packet_font_code); - do_store_four(ff); - append_packet(packet_char_code); - do_store_four(n); - } else if (lua_key_eq(s, comment) || lua_key_eq(s, nop)) { - cmd = packet_nop_code; - } else if (lua_key_eq(s, node)) { - cmd = packet_node_code; - } else if (lua_key_eq(s, push)) { - cmd = packet_push_code; - } else if (lua_key_eq(s, pop)) { - cmd = packet_pop_code; - } else if (lua_key_eq(s, rule)) { - cmd = packet_rule_code; - } else if (lua_key_eq(s, right)) { - cmd = packet_right_code; - } else if (lua_key_eq(s, down)) { - cmd = packet_down_code; - } else if (lua_key_eq(s, special)) { - cmd = packet_special_code; - } else if (lua_key_eq(s, image)) { - cmd = packet_image_code; - } else if (lua_key_eq(s, scale)) { - cmd = packet_scale_code; - } else if (lua_key_eq(s, lua)) { - cmd = packet_lua_code; - } - switch (cmd) { - case packet_push_code: - case packet_pop_code: - append_packet(cmd); - break; - case packet_font_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - n = (int) luaL_checkinteger(L, -1); - if (n == 0) { - ff = n; - } else { - ff = (n > max_f ? l_fonts[1] : l_fonts[n]); - } - do_store_four(ff); - lua_pop(L, 1); - break; - case packet_node_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - n = copy_node_list(nodelist_from_lua(L)); - do_store_four(n); - lua_pop(L, 1); - break; - case packet_char_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - n = (int) luaL_checkinteger(L, -1); - do_store_four(n); - lua_pop(L, 1); - break; - case packet_right_code: - case packet_down_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - n = (int) luaL_checkinteger(L, -1); - do_store_four(sp_to_dvi(n, atsize)); - lua_pop(L, 1); - break; - case packet_rule_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - n = (int) luaL_checkinteger(L, -1); - do_store_four(sp_to_dvi(n, atsize)); - lua_rawgeti(L, -3, 3); - n = (int) luaL_checkinteger(L, -1); - do_store_four(sp_to_dvi(n, atsize)); - lua_pop(L, 2); - break; - case packet_special_code: - case packet_lua_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - s = luaL_checklstring(L, -1, &l); - if (l > 0) { - do_store_four(l); - m = (int) l; - while (m > 0) { - n = *s++; - m--; - append_packet(n); - } - } - lua_pop(L, 1); - break; - case packet_image_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); /* img/imgtable? ... */ - if (lua_istable(L, -1)) { /* imgtable ... */ - lua_getglobal(L, "img"); /* imglib imgtable ... */ - lua_pushstring(L, "new"); /* `new' imglib imgtable ... */ - lua_gettable(L, -2); /* f imglib imgtable ... */ - lua_insert(L, -3); /* imglib imgtable f ... */ - lua_pop(L, 1); /* imgtable f ... */ - lua_call(L, 1, 1); - } /* img ... */ - luaL_checkudata(L, -1, TYPE_IMG); /* img ... --- just typecheck */ - n = luaL_ref(L, LUA_REGISTRYINDEX); /* ... */ - do_store_four(n); - break; - case packet_nop_code: - break; - case packet_scale_code: - append_packet(cmd); - lua_rawgeti(L, -2, 2); - append_float(&cp, (float) luaL_checknumber(L, -1)); - lua_pop(L, 1); - break; - default: - normal_error("vf command","invalid packet code"); - /* fprintf(stdout, "Unknown char packet code %s\n", s); */ - } - } - lua_pop(L, 1); /* command code */ - } else { - normal_error("vf command","commands has to be a tbale"); - /* fprintf(stdout, "Found a `commands' item that is not a table\n"); */ - } - lua_pop(L, 1); /* command table */ - } - append_packet(packet_end_code); - set_charinfo_packets(co, cpackets); - return; -} - -@ @c -static void read_lua_cidinfo(lua_State * L, int f) -{ - int i; - char *s; - /*lua_getfield(L, -1, "cidinfo");*/ - lua_key_rawgeti(cidinfo); - if (lua_istable(L, -1)) { - i = lua_numeric_field_by_index(L,lua_key_index(version), 0); - set_font_cidversion(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(supplement), 0); - set_font_cidsupplement(f, i); - s = n_string_field_copy(L, lua_key_index(registry), "Adobe"); /* Adobe-Identity-0 */ - set_font_cidregistry(f, s); - s = n_string_field_copy(L, lua_key_index(ordering), "Identity"); - set_font_cidordering(f, s); - } - lua_pop(L, 1); -} - - -@ @c -static void read_lua_parameters(lua_State * L, int f) -{ - int i, n, t; - const char *s; - /*lua_getfield(L, -1, "parameters");*/ - lua_key_rawgeti(parameters); - if (lua_istable(L, -1)) { - /* the number of parameters is the max(IntegerKeys(L)),7) */ - n = 7; - lua_pushnil(L); /* first key */ - while (lua_next(L, -2) != 0) { - if (lua_type(L, -2) == LUA_TNUMBER) { - i = (int) lua_tointeger(L, -2); - if (i > n) - n = i; - } - lua_pop(L, 1); /* pop value */ - } - if (n > 7) - set_font_params(f, n); - /* sometimes it is handy to have all integer keys */ - for (i = 1; i <= 7; i++) { - lua_rawgeti(L, -1, i); - if (lua_type(L, -1) == LUA_TNUMBER) { - n = lua_roundnumber(L, -1); /* round ? */ - set_font_param(f, i, n); - } - lua_pop(L, 1); - } - lua_pushnil(L); /* first key */ - while (lua_next(L, -2) != 0) { - t = lua_type(L,-2); - if (t == LUA_TNUMBER) { - i = (int) lua_tointeger(L, -2); - if (i >= 8) { - if (lua_type(L,-1) == LUA_TNUMBER) { - n = lua_roundnumber(L, -1); - } else { - n = 0; - } - set_font_param(f, i, n); - } - } else if (t == LUA_TSTRING) { - s = lua_tostring(L, -2); - if (lua_type(L,-1) == LUA_TNUMBER) { - n = lua_roundnumber(L, -1); - } else { - n = 0; - } - if (lua_key_eq(s, slant)) { - set_font_param(f, slant_code, n); - } else if (lua_key_eq(s, space)) { - set_font_param(f, space_code, n); - } else if (lua_key_eq(s, space_stretch)) { - set_font_param(f, space_stretch_code, n); - } else if (lua_key_eq(s, space_shrink)) { - set_font_param(f, space_shrink_code, n); - } else if (lua_key_eq(s, x_height)) { - set_font_param(f, x_height_code, n); - } else if (lua_key_eq(s, quad)) { - set_font_param(f, quad_code, n); - } else if (lua_key_eq(s, extra_space)) { - set_font_param(f, extra_space_code, n); - } - } - lua_pop(L, 1); - } - } - lua_pop(L, 1); - -} - -@ @c -static void read_lua_math_parameters(lua_State * L, int f) -{ - int i = 0, n = 0, t; - /*lua_getfield(L, -1, "MathConstants");*/ - lua_key_rawgeti(MathConstants); - if (lua_istable(L, -1)) { - lua_pushnil(L); - while (lua_next(L, -2) != 0) { - t = lua_type(L,-2); - if (t == LUA_TNUMBER) { - i = (int) lua_tointeger(L, -2); - } else if (t == LUA_TSTRING) { - i = ff_checkoption(L, -2, NULL, MATH_param_names); - } - n = (int) lua_roundnumber(L, -1); - if (i > 0) { - set_font_math_param(f, i, n); - } - lua_pop(L, 1); /* pop value */ - } - set_font_oldmath(f,false); - } else { - set_font_oldmath(f,true); - } - lua_pop(L, 1); -} - -@ @c -#define MIN_INF -0x7FFFFFFF - -static void store_math_kerns(lua_State * L, int index, charinfo * co, int id) -{ - int l, k; - scaled ht, krn; - lua_key_direct_rawgeti(index); - if (lua_istable(L, -1) && ((k = (int) lua_rawlen(L, -1)) > 0)) { - for (l = 0; l < k; l++) { - lua_rawgeti(L, -1, (l + 1)); - if (lua_istable(L, -1)) { - ht = (scaled) lua_numeric_field_by_index(L, lua_key_index(height), MIN_INF); - krn = (scaled) lua_numeric_field_by_index(L, lua_key_index(kern), MIN_INF); - if (krn > MIN_INF && ht > MIN_INF) - add_charinfo_math_kern(co, id, ht, krn); - } - lua_pop(L, 1); - } - } - lua_pop(L, 1); -} - -@ @c -static void font_char_from_lua(lua_State * L, internal_font_number f, int i, int *l_fonts, boolean has_math) -{ - int k, r, t, lt, u, n; - charinfo *co; - kerninfo *ckerns; - liginfo *cligs; - scaled j; - const char *s; - int nl = 0; /* number of ligature table items */ - int nk = 0; /* number of kern table items */ - int ctr = 0; - int atsize = font_size(f); - if (lua_istable(L, -1)) { - co = get_charinfo(f, i); - set_charinfo_tag(co, 0); - j = lua_numeric_field_by_index(L, lua_key_index(width), 0); - set_charinfo_width(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(height), 0); - set_charinfo_height(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(depth), 0); - set_charinfo_depth(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(italic), 0); - set_charinfo_italic(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(vert_italic), 0); - set_charinfo_vert_italic(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(index), 0); - set_charinfo_index(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(expansion_factor), 1000); - set_charinfo_ef(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(left_protruding), 0); - set_charinfo_lp(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(right_protruding), 0); - set_charinfo_rp(co, j); - k = n_boolean_field(L, lua_key_index(used), 0); - set_charinfo_used(co, k); - s = n_string_field(L, lua_key_index(name)); - if (s != NULL) - set_charinfo_name(co, xstrdup(s)); - else - set_charinfo_name(co, NULL); - /* n_string_field leaves a value on stack*/ - lua_pop(L,1); - u = n_some_field(L,lua_key_index(tounicode)); - if (u == LUA_TNUMBER) { - u = lua_tointeger(L,-1); - if (u < 0) { - set_charinfo_tounicode(co, NULL); - } else if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) { - char *s = malloc(5); - sprintf(s,"%04X",(unsigned int) u); - set_charinfo_tounicode(co,s); - } else { - char *s = malloc(9); - u = u - 0x10000; - sprintf(s,"%04X%04X",(unsigned int) (floor(u/1024)+0xD800),(unsigned int) (u%1024+0xDC00)); - set_charinfo_tounicode(co,s); - } - } else if (u == LUA_TTABLE) { - n = lua_rawlen(L,-1); - u = 0; - for (k = 1; k <= n; k++) { - lua_rawgeti(L, -1, k); - if (lua_type(L,-1) == LUA_TNUMBER) { - u = lua_tointeger(L,-1); - } else { - lua_pop(L, 1); - break; - } - if (u < 0) { - u = -1; - lua_pop(L, 1); - break; - } else if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) { - u = u + 4; - } else { - u = u + 8; - } - lua_pop(L, 1); - } - if (u>0) { - char *s = malloc(u+1); - char *t = s ; - for (k = 1; k <= n; k++) { - lua_rawgeti(L, -1, k); - u = lua_tointeger(L,-1); - if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) { - sprintf(t,"%04X",(unsigned int) u); - t += 4; - } else { - u = u - 0x10000; - sprintf(t,"%04X%04X",(unsigned int) (floor(u/1024)+0xD800),(unsigned int) (u%1024+0xDC00)); - t += 8; - } - lua_pop(L, 1); - } - set_charinfo_tounicode(co,s); - } else { - set_charinfo_tounicode(co, NULL); - } - } else if (u == LUA_TSTRING) { - s = lua_tostring(L,-1); - set_charinfo_tounicode(co, xstrdup(s)); - } else { - set_charinfo_tounicode(co, NULL); - } - /* ... leaves a value on stack*/ - lua_pop(L,1); - - if (has_math) { - j = lua_numeric_field_by_index(L, lua_key_index(top_accent), INT_MIN); - set_charinfo_top_accent(co, j); - j = lua_numeric_field_by_index(L, lua_key_index(bot_accent), INT_MIN); - set_charinfo_bot_accent(co, j); - k = lua_numeric_field_by_index(L, lua_key_index(next), -1); - if (k >= 0) { - set_charinfo_tag(co, list_tag); - set_charinfo_remainder(co, k); - } - - lua_key_rawgeti(extensible); - if (lua_istable(L, -1)) { - int top, bot, mid, rep; - top = lua_numeric_field_by_index(L, lua_key_index(top), 0); - bot = lua_numeric_field_by_index(L, lua_key_index(bot), 0); - mid = lua_numeric_field_by_index(L, lua_key_index(mid), 0); - rep = lua_numeric_field_by_index(L, lua_key_index(rep), 0); - if (top != 0 || bot != 0 || mid != 0 || rep != 0) { - set_charinfo_tag(co, ext_tag); - set_charinfo_extensible(co, top, bot, mid, rep); - } else { - formatted_warning("font", "lua-loaded font %s char U+%X has an invalid extensible field", - font_name(f), (int) i); - } - } - lua_pop(L, 1); - - lua_key_rawgeti(horiz_variants); - if (lua_istable(L, -1)) { - int glyph, startconnect, endconnect, advance, extender; - extinfo *h; - set_charinfo_tag(co, ext_tag); - set_charinfo_hor_variants(co, NULL); - for (k = 1;; k++) { - lua_rawgeti(L, -1, k); - if (lua_istable(L, -1)) { - glyph = lua_numeric_field_by_index(L, lua_key_index(glyph), 0); - extender = lua_numeric_field_by_index(L, lua_key_index(extender), 0); - startconnect = lua_numeric_field_by_index(L, lua_key_index(start), 0); - endconnect = lua_numeric_field_by_index(L, lua_key_index(end), 0); - advance = lua_numeric_field_by_index(L, lua_key_index(advance), 0); - h = new_variant(glyph, startconnect, endconnect, advance, extender); - add_charinfo_hor_variant(co, h); - lua_pop(L, 1); - } else { - lua_pop(L, 1); - break; - } - } - } - lua_pop(L, 1); - - lua_key_rawgeti(vert_variants); - if (lua_istable(L, -1)) { - int glyph, startconnect, endconnect, advance, extender; - extinfo *h; - set_charinfo_tag(co, ext_tag); - set_charinfo_vert_variants(co, NULL); - for (k = 1;; k++) { - lua_rawgeti(L, -1, k); - if (lua_istable(L, -1)) { - glyph = lua_numeric_field_by_index(L, lua_key_index(glyph), 0); - extender = lua_numeric_field_by_index(L, lua_key_index(extender), 0); - startconnect = lua_numeric_field_by_index(L, lua_key_index(start), 0); - endconnect = lua_numeric_field_by_index(L, lua_key_index(end), 0); - advance = lua_numeric_field_by_index(L, lua_key_index(advance), 0); - h = new_variant(glyph, startconnect, endconnect, advance, extender); - add_charinfo_vert_variant(co, h); - lua_pop(L, 1); - } else { - lua_pop(L, 1); - break; - } - } - } - lua_pop(L, 1); - - /* - Here is a complete example: - - |mathkern = {| - | bottom_left = { { height = 420, kern = 80 }, { height = 520, kern = 4 } },| - | bottom_right = { { height = 0, kern = 48 } },| - | top_left = { { height = 620, kern = 0 }, { height = 720, kern = -80 } },| - | top_right = { { height = 676, kern = 115 }, { height = 776, kern = 45 } },| - |}| - - */ - - lua_key_rawgeti(mathkern); - if (lua_istable(L, -1)) { - store_math_kerns(L,lua_key_index(top_left), co, top_left_kern); - store_math_kerns(L,lua_key_index(top_right), co, top_right_kern); - store_math_kerns(L,lua_key_index(bottom_right), co, bottom_right_kern); - store_math_kerns(L,lua_key_index(bottom_left), co, bottom_left_kern); - } - lua_pop(L, 1); - } - /* end of |has_math| */ - count_hash_items(L, kerns, nk); - if (nk > 0) { - /* kerns table still on stack */ - ckerns = xcalloc((unsigned) (nk + 1), sizeof(kerninfo)); - ctr = 0; - lua_pushnil(L); /* traverse hash */ - while (lua_next(L, -2) != 0) { - k = non_boundarychar; - lt = lua_type(L,-2); - if (lt == LUA_TNUMBER) { - k = (int) lua_tointeger(L, -2); /* adjacent char */ - if (k < 0) - k = non_boundarychar; - } else if (lt == LUA_TSTRING) { - s = lua_tostring(L, -2); - if (lua_key_eq(s, right_boundary)) { - k = right_boundarychar; - if (!has_right_boundary(f)) - set_right_boundary(f, get_charinfo(f, right_boundarychar)); - } - } - j = lua_roundnumber(L, -1); /* movement */ - if (k != non_boundarychar) { - set_kern_item(ckerns[ctr], k, j); - ctr++; - } else { - formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kern field", - font_name(f), (int) i); - } - lua_pop(L, 1); - } - /* guard against empty tables */ - if (ctr > 0) { - set_kern_item(ckerns[ctr], end_kern, 0); - set_charinfo_kerns(co, ckerns); - } else { - formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kerns field", - font_name(f), (int) i); - } - lua_pop(L, 1); - } - - /* packet commands */ - lua_key_rawgeti(commands); - if (lua_istable(L, -1)) { - lua_pushnil(L); /* first key */ - if (lua_next(L, -2) != 0) { - lua_pop(L, 2); - read_char_packets(L, (int *) l_fonts, co, f, atsize); - } - } - lua_pop(L, 1); - - /* ligatures */ - count_hash_items(L, ligatures, nl); - if (nl > 0) { - /* ligatures table still on stack */ - cligs = xcalloc((unsigned) (nl + 1), sizeof(liginfo)); - ctr = 0; - lua_pushnil(L); /* traverse hash */ - while (lua_next(L, -2) != 0) { - k = non_boundarychar; - lt = lua_type(L,-2); - if (lt == LUA_TNUMBER) { - k = (int) lua_tointeger(L, -2); /* adjacent char */ - if (k < 0) { - k = non_boundarychar; - } - } else if (lt == LUA_TSTRING) { - s = lua_tostring(L, -2); - if (lua_key_eq(s, right_boundary)) { - k = right_boundarychar; - if (!has_right_boundary(f)) - set_right_boundary(f, get_charinfo(f, right_boundarychar)); - } - } - r = -1; - if (lua_istable(L, -1)) { - r = lua_numeric_field_by_index(L, lua_key_index(char), -1); /* ligature */ - } - if (r != -1 && k != non_boundarychar) { - t = n_enum_field(L, lua_key_index(type), 0, ligature_type_strings); - set_ligature_item(cligs[ctr], (char) ((t * 2) + 1), k, r); - ctr++; - } else { - formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligature field", - font_name(f), (int) i); - } - lua_pop(L, 1); /* iterator value */ - } - /* guard against empty tables */ - if (ctr > 0) { - set_ligature_item(cligs[ctr], 0, end_ligature, 0); - set_charinfo_ligatures(co, cligs); - } else { - formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligatures field", - font_name(f), (int) i); - } - lua_pop(L, 1); /* ligatures table */ - } - } -} - -@ The caller has to fix the state of the lua stack when there is an error! - -@c -int font_from_lua(lua_State * L, int f) -{ - int i, n, r, t, lt; - int s_top; /* lua stack top */ - int bc; /* first char index */ - int ec; /* last char index */ - char *s; - const char *ss; - int *l_fonts = NULL; - int save_ref ; - boolean no_math = false; - - /* will we save a cache of the luat table? */ - - save_ref = 1; /* we start with ss = "yes" */ - ss = NULL; - ss = n_string_field(L, lua_key_index(cache)); - if (lua_key_eq(ss, no)) - save_ref = -1; - else if (lua_key_eq(ss, renew)) - save_ref = 0; - /* n_string_field leaves a value on stack*/ - lua_pop(L,1); - - /* the table is at stack index -1 */ - - s = n_string_field_copy(L,lua_key_index(area), ""); - set_font_area(f, s); - s = n_string_field_copy(L, lua_key_index(filename), NULL); - set_font_filename(f, s); - s = n_string_field_copy(L, lua_key_index(encodingname), NULL); - set_font_encodingname(f, s); - - s = n_string_field_copy(L, lua_key_index(name), NULL); - set_font_name(f, s); - s = n_string_field_copy(L, lua_key_index(fullname), font_name(f)); - set_font_fullname(f, s); - - if (s == NULL) { - formatted_error("font","lua-loaded font '%d' has no name!", f); - return false; - } - s = n_string_field_copy(L, lua_key_index(psname), NULL); - set_font_psname(f, s); - - i = lua_numeric_field_by_index(L,lua_key_index(units_per_em), 0); - set_font_units_per_em(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(designsize), 655360); - set_font_dsize(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(size), font_dsize(f)); - set_font_size(f, i); - set_font_checksum(f, (unsigned)(lua_unsigned_numeric_field_by_index(L,lua_key_index(checksum), 0))) ; - i = lua_numeric_field_by_index(L,lua_key_index(direction), 0); - set_font_natural_dir(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(encodingbytes), 0); - set_font_encodingbytes(f, (char) i); - i = n_boolean_field(L,lua_key_index(oldmath), 0); - set_font_oldmath(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(tounicode), 0); - set_font_tounicode(f, (char) i); - - i = lua_numeric_field_by_index(L,lua_key_index(extend), 1000); - if (i < FONT_EXTEND_MIN) - i = FONT_EXTEND_MIN; - if (i > FONT_EXTEND_MAX) - i = FONT_EXTEND_MAX; - set_font_extend(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(slant), 0); - if (i < FONT_SLANT_MIN) - i = FONT_SLANT_MIN; - if (i > FONT_SLANT_MAX) - i = FONT_SLANT_MAX; - set_font_slant(f, i); - - i = lua_numeric_field_by_index(L,lua_key_index(hyphenchar), default_hyphen_char_par); - set_hyphen_char(f, i); - i = lua_numeric_field_by_index(L,lua_key_index(skewchar), default_skew_char_par); - set_skew_char(f, i); - i = n_boolean_field(L, lua_key_index(used), 0); - set_font_used(f, (char) i); - - s = n_string_field_copy(L, lua_key_index(attributes), NULL); - if (s != NULL && strlen(s) > 0) { - i = maketexstring(s); - set_pdf_font_attr(f, i); - } - free(s); - - i = n_enum_field(L, lua_key_index(type), unknown_font_type, font_type_strings); - set_font_type(f, i); - i = n_enum_field(L, lua_key_index(format), unknown_format, font_format_strings); - set_font_format(f, i); - i = n_enum_field(L, lua_key_index(writingmode), unknown_writingmode, font_writingmode_strings); - set_font_writingmode(f, i); - i = n_enum_field(L, lua_key_index(identity), unknown_identity, font_identity_strings); - set_font_identity(f, i); - i = n_enum_field(L, lua_key_index(embedding), unknown_embedding, font_embedding_strings); - set_font_embedding(f, i); - if (font_encodingbytes(f) == 0 && (font_format(f) == opentype_format || font_format(f) == truetype_format)) { - set_font_encodingbytes(f, 2); - } - - /* now fetch the base fonts, if needed */ - count_hash_items(L, fonts, n); - if (n > 0) { - /* font table still on stack */ - l_fonts = xmalloc((unsigned) ((unsigned) (n + 2) * sizeof(int))); - memset(l_fonts, 0, (size_t) ((unsigned) (n + 2) * sizeof(int))); - for (i = 1; i <= n; i++) { - lua_rawgeti(L, -1, i); - if (lua_istable(L, -1)) { - lua_key_rawgeti(id); - if (lua_isnumber(L, -1)) { - l_fonts[i] = (int) lua_tointeger(L, -1); - if (l_fonts[i] == 0) { - l_fonts[i] = (int) f; - } - lua_pop(L, 2); /* pop id and entry */ - continue; - } - lua_pop(L, 1); /* pop id */ - }; - ss = NULL; - if (lua_istable(L, -1)) { - ss = n_string_field(L, lua_key_index(name)); - /* string is anchored */ - lua_pop(L,1); - } - if (ss != NULL) { - t = lua_numeric_field_by_index(L, lua_key_index(size), -1000); - /* TODO: the stack is messed up, otherwise this explicit resizing would not be needed */ - s_top = lua_gettop(L); - if (strcmp(font_name(f), ss) == 0) - l_fonts[i] = f; - else - l_fonts[i] = find_font_id(ss, t); - lua_settop(L, s_top); - } else { - formatted_error("font","invalid local font in lua-loaded font '%s'", font_name(f)); - } - lua_pop(L, 1); /* pop list entry */ - } - lua_pop(L, 1); /* pop font table */ - } else if (font_type(f) == virtual_font_type) { - formatted_error("font","invalid local fonts in lua-loaded font '%s'", font_name(f)); - } else { - l_fonts = xmalloc(3 * sizeof(int)); - l_fonts[0] = 0; - l_fonts[1] = f; - l_fonts[2] = 0; - } - - /* parameters */ - no_math = n_boolean_field(L, lua_key_index(nomath), 0); - read_lua_parameters(L, f); - if (!no_math) { - read_lua_math_parameters(L, f); - if (n_boolean_field(L, lua_key_index(oldmath), 0)) { - set_font_oldmath(f,true); - } - - } else { - set_font_oldmath(f,true); - } - read_lua_cidinfo(L, f); - - /* characters */ - lua_key_rawgeti(characters); - if (lua_istable(L, -1)) { - /* find the array size values */ - int num = 0; /* number of charinfo's to add */ - ec = 0; - bc = -1; - lua_pushnil(L); /* first key */ - while (lua_next(L, -2) != 0) { - if (lua_isnumber(L, -2)) { - i = (int) lua_tointeger(L, -2); - if (i >= 0) { - if (lua_istable(L, -1)) { - num++; - if (i > ec) - ec = i; - if (bc < 0) - bc = i; - if (bc >= 0 && i < bc) - bc = i; - } - } - } - lua_pop(L, 1); - } - if (bc != -1) { - font_malloc_charinfo(f, num); - set_font_bc(f, bc); - set_font_ec(f, ec); - lua_pushnil(L); /* first key */ - while (lua_next(L, -2) != 0) { - lt = lua_type(L,-2); - if (lt == LUA_TNUMBER) { - i = (int) lua_tointeger(L, -2); - if (i >= 0) { - font_char_from_lua(L, f, i, l_fonts, !no_math); - } - } else if (lt == LUA_TSTRING) { - const char *ss1 = lua_tostring(L, -2); - if (lua_key_eq(ss1, left_boundary)) { - font_char_from_lua(L, f, left_boundarychar, l_fonts, - !no_math); - } else if (lua_key_eq(ss1, right_boundary)) { - font_char_from_lua(L, f, right_boundarychar, l_fonts, - !no_math); - } - } - lua_pop(L, 1); - } - lua_pop(L, 1); - - /* - Handle font expansion last: the |copy_font| routine is called eventually, - and that needs to know |bc| and |ec|. - */ - - if (font_type(f) != virtual_font_type) { - int fstep = lua_numeric_field_by_index(L, lua_key_index(step), 0); - if (fstep < 0) - fstep = 0; - if (fstep > 100) - fstep = 100; - if (fstep != 0) { - int fshrink = lua_numeric_field_by_index(L, lua_key_index(shrink), 0); - int fstretch =lua_numeric_field_by_index(L, lua_key_index(stretch), 0); - int fexpand = n_boolean_field(L, lua_key_index(auto_expand), 0); - if (fshrink < 0) - fshrink = 0; - if (fshrink > 500) - fshrink = 500; - fshrink -= (fshrink % fstep); - if (fshrink < 0) - fshrink = 0; - if (fstretch < 0) - fstretch = 0; - if (fstretch > 1000) - fstretch = 1000; - fstretch -= (fstretch % fstep); - if (fstretch < 0) - fstretch = 0; - set_expand_params(f, fexpand, fstretch, fshrink, fstep); - } - } - - } else { - /* jikes, no characters */ - formatted_warning("font","lua-loaded font '%d' with name '%s' has no characters", f, font_name(f)); - } - - if (save_ref > 0) { - r = luaL_ref(L, LUA_REGISTRYINDEX); /* pops the table */ - set_font_cache_id(f, r); - } else { - lua_pop(L, 1); - set_font_cache_id(f, save_ref); - } - } else { - /* jikes, no characters */ - formatted_warning("font","lua-loaded font '%d' with name '%s' has no character table", f, font_name(f)); - } - if (l_fonts != NULL) - free(l_fonts); - return true; -} - -@* Ligaturing. - -@c -static void nesting_append(halfword nest1, halfword newn) -{ - halfword tail = tlink(nest1); - if (tail == null) { - couple_nodes(nest1, newn); - } else { - couple_nodes(tail, newn); - } - tlink(nest1) = newn; -} - -static void nesting_prepend(halfword nest1, halfword newn) -{ - halfword head = vlink(nest1); - couple_nodes(nest1, newn); - if (head == null) { - tlink(nest1) = newn; - } else { - couple_nodes(newn, head); - } -} - -static void nesting_prepend_list(halfword nest1, halfword newn) -{ - halfword head = vlink(nest1); - couple_nodes(nest1, newn); - if (head == null) { - tlink(nest1) = tail_of_list(newn); - } else { - halfword tail = tail_of_list(newn); - couple_nodes(tail, head); - } -} - -static int test_ligature(liginfo * lig, halfword left, halfword right) -{ - if (type(left) != glyph_node) - return 0; - if (font(left) != font(right)) - return 0; - if (is_ghost(left) || is_ghost(right)) - return 0; - *lig = get_ligature(font(left), character(left), character(right)); - if (is_valid_ligature(*lig)) { - return 1; - } - return 0; -} - -static int try_ligature(halfword * frst, halfword fwd) -{ - halfword cur = *frst; - liginfo lig; - if (test_ligature(&lig, cur, fwd)) { - int move_after = (lig_type(lig) & 0x0C) >> 2; - int keep_right = ((lig_type(lig) & 0x01) != 0); - int keep_left = ((lig_type(lig) & 0x02) != 0); - halfword newgl = raw_glyph_node(); - font(newgl) = font(cur); - character(newgl) = lig_replacement(lig); - set_is_ligature(newgl); - /* - below might not be correct in contrived border case. - but we use it only for debugging, so ... - */ - if (character(cur) < 0) { - set_is_leftboundary(newgl); - } - if (character(fwd) < 0) { - set_is_rightboundary(newgl); - } - if (character(cur) < 0) { - if (character(fwd) < 0) { - build_attribute_list(newgl); - } else { - add_node_attr_ref(node_attr(fwd)); - node_attr(newgl) = node_attr(fwd); - } - } else { - add_node_attr_ref(node_attr(cur)); - node_attr(newgl) = node_attr(cur); - } - - /* - TODO/FIXME if this ligature is consists of another ligature - we should add it's |lig_ptr| to the new glyphs |lig_ptr| (and - cleanup the no longer needed node) LOW PRIORITY - */ - /* left side */ - if (keep_left) { - halfword new_first = copy_node(cur); - lig_ptr(newgl) = new_first; - couple_nodes(cur, newgl); - if (move_after) { - move_after--; - cur = newgl; - } - } else { - halfword prev = alink(cur); - uncouple_node(cur); - lig_ptr(newgl) = cur; - couple_nodes(prev, newgl); - cur = newgl; /* as cur has disappeared */ - } - /* right side */ - if (keep_right) { - halfword new_second = copy_node(fwd); - /* correct, because we {\it know\/} |lig_ptr| points to {\it one\/} node */ - couple_nodes(lig_ptr(newgl), new_second); - couple_nodes(newgl, fwd); - if (move_after) { - move_after--; - cur = fwd; - } - } else { - halfword next = vlink(fwd); - uncouple_node(fwd); - /* correct, because we {\it know\/} |lig_ptr| points to {\it one\/} node */ - couple_nodes(lig_ptr(newgl), fwd); - if (next != null) { - couple_nodes(newgl, next); - } - } - /* check and return */ - *frst = cur; - return 1; - } - return 0; -} - -@ there shouldn't be any ligatures here - we only add them at the end of - |xxx_break| in a \.{DISC-1 - DISC-2} situation and we stop processing \.{DISC-1} - (we continue with \.{DISC-1}'s |post_| and |no_break|. - -@c -static halfword handle_lig_nest(halfword root, halfword cur) -{ - if (cur == null) - return root; - while (vlink(cur) != null) { - halfword fwd = vlink(cur); - if (type(cur) == glyph_node && type(fwd) == glyph_node && - font(cur) == font(fwd) && try_ligature(&cur, fwd)) { - continue; - } - cur = vlink(cur); - } - tlink(root) = cur; - return root; -} - -static halfword handle_lig_word(halfword cur) -{ - halfword right = null; - if (type(cur) == boundary_node) { - halfword prev = alink(cur); - halfword fwd = vlink(cur); - /* no need to uncouple |cur|, it is freed */ - flush_node(cur); - if (fwd == null) { - vlink(prev) = fwd; - return prev; - } - couple_nodes(prev, fwd); - if (type(fwd) != glyph_node) - return prev; - cur = fwd; - } else if (has_left_boundary(font(cur))) { - halfword prev = alink(cur); - halfword p = new_glyph(font(cur), left_boundarychar); - couple_nodes(prev, p); - couple_nodes(p, cur); - cur = p; - } - if (has_right_boundary(font(cur))) { - right = new_glyph(font(cur), right_boundarychar); - } - while (1) { - /* A glyph followed by ... */ - if (type(cur) == glyph_node) { - halfword fwd = vlink(cur); - if (fwd == null) { /* last character of paragraph */ - if (right == null) - break; - /* \.{--\\par} prohibits use of |couple_nodes| here */ - try_couple_nodes(cur, right); - right = null; - continue; - } - if (type(fwd) == glyph_node) { /* |GLYPH - GLYPH| */ - if (font(cur) != font(fwd)) - break; - if (try_ligature(&cur, fwd)) - continue; - } else if (type(fwd) == disc_node) { /* |GLYPH - DISC| */ - - /* if \.{a{bx}{}{y}} and \.{a+b=>B} convert to \.{{Bx}{}{ay}} */ - halfword pre = vlink_pre_break(fwd); - halfword nob = vlink_no_break(fwd); - halfword next, tail; - liginfo lig; - /* Check on: a{b?}{?}{?} and a+b=>B : {B?}{?}{a?} */ - /* Check on: a{?}{?}{b?} and a+b=>B : {a?}{?}{B?} */ - if ((pre != null && type(pre) == glyph_node - && test_ligature(&lig, cur, pre)) - || (nob != null && type(nob) == glyph_node - && test_ligature(&lig, cur, nob))) { - /* move cur from before disc, to skipped part */ - halfword prev = alink(cur); - uncouple_node(cur); - couple_nodes(prev, fwd); - nesting_prepend(no_break(fwd), cur); - /* now ligature the |pre_break| */ - nesting_prepend(pre_break(fwd), copy_node(cur)); - /* As we have removed cur, we need to start again ... */ - cur = prev; - } - /* Check on: a{?}{?}{}b and a+b=>B : {a?}{?b}{B} */ - next = vlink(fwd); - if (nob == null && next != null && type(next) == glyph_node - && test_ligature(&lig, cur, next)) { - /* move |cur| from before |disc| to |no_break| part */ - halfword prev = alink(cur); - uncouple_node(cur); - couple_nodes(prev, fwd); - couple_nodes(no_break(fwd), cur); /* we {\it know\/} it's empty */ - /* now copy cur the |pre_break| */ - nesting_prepend(pre_break(fwd), copy_node(cur)); - /* move next from after disc to |no_break| part */ - tail = vlink(next); - uncouple_node(next); - try_couple_nodes(fwd, tail); - couple_nodes(cur, next); /* we {\it know\/} this works */ - tlink(no_break(fwd)) = next; /* and make sure the list is correct */ - /* now copy next to the |post_break| */ - nesting_append(post_break(fwd), copy_node(next)); - /* As we have removed cur, we need to start again ... */ - cur = prev; - } - /* we are finished with the |pre_break| */ - handle_lig_nest(pre_break(fwd), vlink_pre_break(fwd)); - } else if (type(fwd) == boundary_node) { - halfword next = vlink(fwd); - try_couple_nodes(cur, next); - flush_node(fwd); - if (right != null) { - flush_node(right); /* Shame, didn't need it */ - /* no need to reset |right|, we're going to leave the loop anyway */ - } - break; - } else { - /* fwd is something unknown */ - if (right == null) - break; - couple_nodes(cur, right); - couple_nodes(right, fwd); - right = null; - continue; - } - /* A discretionary followed by ... */ - } else if (type(cur) == disc_node) { - /* If \.{{?}{x}{?}} or \.{{?}{?}{y}} then ... */ - if (vlink_no_break(cur) != null || vlink_post_break(cur) != null) { - halfword prev = 0; - halfword fwd; - liginfo lig; - if (subtype(cur) == select_disc) { - prev = alink(cur); - if (vlink_post_break(cur) != null) - handle_lig_nest(post_break(prev), vlink_post_break(prev)); - if (vlink_no_break(cur) != null) - handle_lig_nest(no_break(prev), vlink_no_break(prev)); - } - if (vlink_post_break(cur) != null) - handle_lig_nest(post_break(cur), vlink_post_break(cur)); - if (vlink_no_break(cur) != null) - handle_lig_nest(no_break(cur), vlink_no_break(cur)); - while ((fwd = vlink(cur)) != null) { - halfword nob, pst, next; - if (type(fwd) != glyph_node) - break; - if (subtype(cur) != select_disc) { - nob = tlink_no_break(cur); - pst = tlink_post_break(cur); - if ((nob == null || !test_ligature(&lig, nob, fwd)) && - (pst == null || !test_ligature(&lig, pst, fwd))) - break; - nesting_append(no_break(cur), copy_node(fwd)); - handle_lig_nest(no_break(cur), nob); - } else { - int dobreak = 0; - nob = tlink_no_break(prev); - pst = tlink_post_break(prev); - if ((nob == null || !test_ligature(&lig, nob, fwd)) && - (pst == null || !test_ligature(&lig, pst, fwd))) - dobreak = 1; - if (!dobreak) { - nesting_append(no_break(prev), copy_node(fwd)); - handle_lig_nest(no_break(prev), nob); - nesting_append(post_break(prev), copy_node(fwd)); - handle_lig_nest(post_break(prev), pst); - } - dobreak = 0; - nob = tlink_no_break(cur); - pst = tlink_post_break(cur); - if ((nob == null || !test_ligature(&lig, nob, fwd)) && - (pst == null || !test_ligature(&lig, pst, fwd))) - dobreak = 1; - if (!dobreak) { - nesting_append(no_break(cur), copy_node(fwd)); - handle_lig_nest(no_break(cur), nob); - } - if (dobreak) - break; - } - next = vlink(fwd); - uncouple_node(fwd); - try_couple_nodes(cur, next); - nesting_append(post_break(cur), fwd); - handle_lig_nest(post_break(cur), pst); - } - if (fwd != null && type(fwd) == disc_node) { - halfword next = vlink(fwd); - if (vlink_no_break(fwd) == null - && vlink_post_break(fwd) == null - && next != null - && type(next) == glyph_node - && ((tlink_post_break(cur) != null && test_ligature(&lig, tlink_post_break(cur), next)) || - (tlink_no_break (cur) != null && test_ligature(&lig, tlink_no_break (cur), next)))) { - /* Building an |init_disc| followed by a |select_disc| - \.{{a-}{b}{AB} {-}{}{}} 'c' - */ - /* is it tail necessary ? */ - halfword last1 = vlink(next), tail ; - uncouple_node(next); - try_couple_nodes(fwd, last1); - /* \.{{a-}{b}{AB} {-}{c}{}} */ - nesting_append(post_break(fwd), copy_node(next)); - /* \.{{a-}{b}{AB} {-}{c}{-}} */ - if (vlink_no_break(cur) != null) { - nesting_prepend(no_break(fwd), copy_node(vlink_pre_break(fwd))); - } - /* \.{{a-}{b}{AB} {b-}{c}{-}} */ - if (vlink_post_break(cur) != null) - nesting_prepend_list(pre_break(fwd), copy_node_list(vlink_post_break(cur))); - /* \.{{a-}{b}{AB} {b-}{c}{AB-}} */ - if (vlink_no_break(cur) != null) { - nesting_prepend_list(no_break(fwd), copy_node_list(vlink_no_break(cur))); - } - /* \.{{a-}{b}{ABC} {b-}{c}{AB-}} */ - tail = tlink_no_break(cur); - nesting_append(no_break(cur), copy_node(next)); - handle_lig_nest(no_break(cur), tail); - /* \.{{a-}{BC}{ABC} {b-}{c}{AB-}} */ - tail = tlink_post_break(cur); - nesting_append(post_break(cur), next); - handle_lig_nest(post_break(cur), tail); - /* and set the subtypes */ - subtype(cur) = init_disc; - subtype(fwd) = select_disc; - } - } - } - - } else { - /* NO GLYPH NOR DISC */ - return cur; - } - /* step-to-next-node */ - /* \.{--\\par} allows |vlink(cur)| to be null */ - cur = vlink(cur); - } - - return cur; -} - -@ Return value is the new tail, head should be a dummy - -@c -halfword handle_ligaturing(halfword head, halfword tail) -{ - halfword save_tail1 = null; /* trick to allow explicit |node==null| tests */ - halfword cur, prev; - - if (vlink(head) == null) - return tail; - if (tail != null) { - save_tail1 = vlink(tail); - vlink(tail) = null; - } - - /* |if (fix_node_lists)| */ - fix_node_list(head); - - prev = head; - cur = vlink(prev); - - while (cur != null) { - if (type(cur) == glyph_node || (type(cur) == boundary_node)) { - cur = handle_lig_word(cur); - } - prev = cur; - cur = vlink(cur); - } - if (prev == null) { - /* hh: looks bad to me */ - prev = tail; - } - - if (tail != null) { - try_couple_nodes(prev, save_tail1); - } - return prev; -} - - -@* Kerning. - -@c -static void add_kern_before(halfword left, halfword right) -{ - if ((!is_rightghost(right)) && - font(left) == font(right) && has_kern(font(left), character(left))) { - int k = raw_get_kern(font(left), character(left), character(right)); - if (k != 0) { - halfword kern = new_kern(k); - halfword prev = alink(right); - couple_nodes(prev, kern); - couple_nodes(kern, right); - /* update the attribute list (inherit from left) */ - delete_attribute_ref(node_attr(kern)); - add_node_attr_ref(node_attr(left)); - node_attr(kern) = node_attr(left); - } - } -} - -static void add_kern_after(halfword left, halfword right, halfword aft) -{ - if ((!is_rightghost(right)) && - font(left) == font(right) && has_kern(font(left), character(left))) { - int k = raw_get_kern(font(left), character(left), character(right)); - if (k != 0) { - halfword kern = new_kern(k); - halfword next = vlink(aft); - couple_nodes(aft, kern); - try_couple_nodes(kern, next); - /* update the attribute list (inherit from left == aft) */ - delete_attribute_ref(node_attr(kern)); - add_node_attr_ref(node_attr(aft)); - node_attr(kern) = node_attr(aft); - } - } -} - -static void do_handle_kerning(halfword root, halfword init_left, halfword init_right) -{ - halfword cur = vlink(root); - halfword left = null; - if (cur == null) { - if (init_left != null && init_right != null) { - add_kern_after(init_left, init_right, root); - tlink(root) = vlink(root); - } - return; - } - if (type(cur) == glyph_node) { - set_is_glyph(cur); - if (init_left != null) - add_kern_before(init_left, cur); - left = cur; - } - while ((cur = vlink(cur)) != null) { - if (type(cur) == glyph_node) { - set_is_glyph(cur); - if (left != null) { - add_kern_before(left, cur); - if (character(left) < 0 || is_ghost(left)) { - halfword prev = alink(left); - couple_nodes(prev, cur); - flush_node(left); - } - } - left = cur; - } else { - if (type(cur) == disc_node) { - halfword right = type(vlink(cur)) == glyph_node ? vlink(cur) : null; - do_handle_kerning(pre_break(cur), left, null); - if (vlink_pre_break(cur) != null) - tlink_pre_break(cur) = tail_of_list(vlink_pre_break(cur)); - do_handle_kerning(post_break(cur), null, right); - if (vlink_post_break(cur) != null) - tlink_post_break(cur) = tail_of_list(vlink_post_break(cur)); - do_handle_kerning(no_break(cur), left, right); - if (vlink_no_break(cur) != null) - tlink_no_break(cur) = tail_of_list(vlink_no_break(cur)); /* needed? */ - } - if (left != null) { - if (character(left) < 0 || is_ghost(left)) { - halfword prev = alink(left); - couple_nodes(prev, cur); - flush_node(left); - } - left = null; - } - } - } - if (left != null) { - if (init_right != null) - add_kern_after(left, init_right, left); - if (character(left) < 0 || is_ghost(left)) { - halfword prev = alink(left); - halfword next = vlink(left); - if (next != null) { - couple_nodes(prev, next); - tlink(root) = next; - } else if (prev != root) { - vlink(prev) = null; - tlink(root) = prev; - } else { - vlink(root) = null; - tlink(root) = null; - } - flush_node(left); - } - } -} - -/* -halfword handle_kerning(halfword head, halfword tail) -{ - halfword save_link; - save_link = vlink(tail); - vlink(tail) = null; - tlink(head) = tail; - do_handle_kerning(head, null, null); - tail = tlink(head); - if (valid_node(save_link)) { - try_couple_nodes(tail, save_link); - } - return tail; -} -*/ - -halfword handle_kerning(halfword head, halfword tail) -{ - halfword save_link = null; - if (tail == null) { - tlink(head) = null; - do_handle_kerning(head, null, null); - } else { - save_link = vlink(tail); - vlink(tail) = null; - tlink(head) = tail; - do_handle_kerning(head, null, null); - tail = tlink(head); - if (valid_node(save_link)) { - try_couple_nodes(tail, save_link); - } - } - return tail; -} - -@* ligaturing and kerning : lua-interface. - -@c -static halfword run_lua_ligkern_callback(halfword head, halfword tail, int callback_id) -{ - int i; - int top = lua_gettop(Luas); - if (!get_callback(Luas, callback_id)) { - lua_pop(Luas, 2); - return tail; - } - nodelist_to_lua(Luas, head); - nodelist_to_lua(Luas, tail); - if ((i=lua_pcall(Luas, 2, 0, 0)) != 0) { - luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); - return tail; - } - fix_node_list(head); - lua_settop(Luas, top); - return tail; -} - -halfword new_ligkern(halfword head, halfword tail) -{ - int callback_id = 0; - if (vlink(head) == null) - return tail; - callback_id = callback_defined(ligaturing_callback); - if (callback_id > 0) { - tail = run_lua_ligkern_callback(head, tail, callback_id); - if (tail == null) - tail = tail_of_list(head); - } else if (callback_id == 0) { - tail = handle_ligaturing(head, tail); - } - callback_id = callback_defined(kerning_callback); - if (callback_id > 0) { - tail = run_lua_ligkern_callback(head, tail, callback_id); - if (tail == null) { - tail = tail_of_list(head); - } - } else if (callback_id == 0) { - halfword nest1 = new_node(nesting_node, 1); - halfword cur = vlink(head); - halfword aft = vlink(tail); - couple_nodes(nest1, cur); - tlink(nest1) = tail; - vlink(tail) = null; - do_handle_kerning(nest1, null, null); - couple_nodes(head, vlink(nest1)); - tail = tlink(nest1); - try_couple_nodes(tail, aft); - flush_node(nest1); - } - return tail; -} +% luafont.w +% +% Copyright 2006-2011 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c +#include "ptexlib.h" +#include "lua/luatex-api.h" + +#define noVERBOSE + +/* todo: also keys */ + +const char *font_type_strings[] = { + "unknown", "virtual", "real", NULL +}; + +const char *font_writingmode_strings[] = { + "unknown", "horizontal", "vertical", NULL +}; + +const char *font_identity_strings[] = { + "unknown", "horizontal", "vertical", NULL +}; + +const char *font_format_strings[] = { + "unknown", "type1", "type3", "truetype", "opentype", NULL +}; + +const char *font_embedding_strings[] = { + "unknown", "no", "subset", "full", NULL +}; + +const char *ligature_type_strings[] = { + "=:", "=:|", "|=:", "|=:|", "", "=:|>", "|=:>", "|=:|>", "", "", "", "|=:|>>", NULL +}; + +const char *MATH_param_names[] = { + "nil", + "ScriptPercentScaleDown", + "ScriptScriptPercentScaleDown", + "DelimitedSubFormulaMinHeight", + "DisplayOperatorMinHeight", + "MathLeading", + "AxisHeight", + "AccentBaseHeight", + "FlattenedAccentBaseHeight", + "SubscriptShiftDown", + "SubscriptTopMax", + "SubscriptBaselineDropMin", + "SuperscriptShiftUp", + "SuperscriptShiftUpCramped", + "SuperscriptBottomMin", + "SuperscriptBaselineDropMax", + "SubSuperscriptGapMin", + "SuperscriptBottomMaxWithSubscript", + "SpaceAfterScript", + "UpperLimitGapMin", + "UpperLimitBaselineRiseMin", + "LowerLimitGapMin", + "LowerLimitBaselineDropMin", + "StackTopShiftUp", + "StackTopDisplayStyleShiftUp", + "StackBottomShiftDown", + "StackBottomDisplayStyleShiftDown", + "StackGapMin", + "StackDisplayStyleGapMin", + "StretchStackTopShiftUp", + "StretchStackBottomShiftDown", + "StretchStackGapAboveMin", + "StretchStackGapBelowMin", + "FractionNumeratorShiftUp", + "FractionNumeratorDisplayStyleShiftUp", + "FractionDenominatorShiftDown", + "FractionDenominatorDisplayStyleShiftDown", + "FractionNumeratorGapMin", + "FractionNumeratorDisplayStyleGapMin", + "FractionRuleThickness", + "FractionDenominatorGapMin", + "FractionDenominatorDisplayStyleGapMin", + "SkewedFractionHorizontalGap", + "SkewedFractionVerticalGap", + "OverbarVerticalGap", + "OverbarRuleThickness", + "OverbarExtraAscender", + "UnderbarVerticalGap", + "UnderbarRuleThickness", + "UnderbarExtraDescender", + "RadicalVerticalGap", + "RadicalDisplayStyleVerticalGap", + "RadicalRuleThickness", + "RadicalExtraAscender", + "RadicalKernBeforeDegree", + "RadicalKernAfterDegree", + "RadicalDegreeBottomRaisePercent", + "MinConnectorOverlap", + "SubscriptShiftDownWithSuperscript", + "FractionDelimiterSize", + "FractionDelimiterDisplayStyleSize", + "NoLimitSubFactor", + "NoLimitSupFactor", + NULL, +}; + +/* here for now, may be useful elsewhere */ + +int ff_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]); + +int ff_checkoption (lua_State *L, int narg, const char *def, const char *const lst[]) { + const char *name = (def) ? luaL_optstring(L, narg, def) : luaL_checkstring(L, narg); + int i; + for (i=0; lst[i]; i++) + if (strcmp(lst[i], name) == 0) + return i; + return -1; +} + +#define dump_intfield(L,n,c) \ + lua_push_string_by_name(L,n); \ + lua_pushinteger(L, c); \ + lua_rawset(L, -3); \ + +#define dump_stringfield(L,n,c) \ + lua_push_string_by_name(L,n); \ + lua_pushstring(L, c); \ + lua_rawset(L, -3); + +#define dump_booleanfield(L,n,c) \ + lua_push_string_by_name(L,n); \ + lua_pushboolean(L, c); \ + lua_rawset(L, -3); + +static void dump_math_kerns(lua_State * L, charinfo * co, int l, int id) +{ + int i; + for (i = 0; i < l; i++) { + lua_newtable(L); + if (id==top_left_kern) { + dump_intfield(L, height, co->top_left_math_kern_array[(2*i)]); + dump_intfield(L, kern, co->top_left_math_kern_array[(2*i)+1]); + } else if (id==top_right_kern) { + dump_intfield(L, height, co->top_right_math_kern_array[(2*i)]); + dump_intfield(L, kern, co->top_right_math_kern_array[(2*i)+1]); + } else if (id==bottom_right_kern) { + dump_intfield(L, height, co->bottom_right_math_kern_array[(2*i)]); + dump_intfield(L, kern, co->bottom_right_math_kern_array[(2*i)+1]); + } else if (id==bottom_left_kern) { + dump_intfield(L, height, co->bottom_left_math_kern_array[(2*i)]); + dump_intfield(L, kern, co->bottom_left_math_kern_array[(2*i)+1]); + } + lua_rawseti(L, -2, (i + 1)); + } +} + +static void font_char_to_lua(lua_State * L, internal_font_number f, charinfo * co) +{ + liginfo *l; + kerninfo *ki; + + lua_createtable(L, 0, 10); + + dump_intfield(L,width,get_charinfo_width(co)); + dump_intfield(L,height,get_charinfo_height(co)); + dump_intfield(L,depth,get_charinfo_depth(co)); + + if (get_charinfo_italic(co) != 0) { + dump_intfield(L,italic,get_charinfo_italic(co)); + } + if (get_charinfo_vert_italic(co) != 0) { + dump_intfield(L,vert_italic,get_charinfo_vert_italic(co)); + } + if (get_charinfo_top_accent(co) !=0 && get_charinfo_top_accent(co) != INT_MIN) { + dump_intfield(L,top_accent,get_charinfo_top_accent(co)); + } + if (get_charinfo_bot_accent(co) != 0 && get_charinfo_bot_accent(co) != INT_MIN) { + dump_intfield(L,bot_accent,get_charinfo_bot_accent(co)); + } + if (get_charinfo_ef(co) != 1000) { + dump_intfield(L,expansion_factor,get_charinfo_ef(co)); + } + if (get_charinfo_lp(co) != 0) { + dump_intfield(L,left_protruding,get_charinfo_lp(co)); + } + if (get_charinfo_rp(co) != 0) { + dump_intfield(L,right_protruding,get_charinfo_rp(co)); + } + if (font_encodingbytes(f) == 2) { + dump_intfield(L,index,get_charinfo_index(co)); + } + if (get_charinfo_name(co) != NULL) { + dump_stringfield(L,name,get_charinfo_name(co)); + } + if (get_charinfo_tounicode(co) != NULL) { + dump_stringfield(L,tounicode,get_charinfo_tounicode(co)); + } + if (get_charinfo_tag(co) == list_tag) { + dump_intfield(L,next,get_charinfo_remainder(co)); + } + if (get_charinfo_used(co)) { + dump_booleanfield(L,used,(get_charinfo_used(co) ? true : false)); + } + if (get_charinfo_tag(co) == ext_tag) { + extinfo *h; + h = get_charinfo_hor_variants(co); + if (h != NULL) { + int i = 1; + lua_push_string_by_name(L,horiz_variants); + lua_newtable(L); + while (h != NULL) { + lua_createtable(L, 0, 5); + dump_intfield(L, glyph, h->glyph); + dump_intfield(L, extender, h->extender); + dump_intfield(L, start, h->start_overlap); + dump_intfield(L, end, h->end_overlap); + dump_intfield(L, advance, h->advance); + lua_rawseti(L, -2, i); + i++; + h = h->next; + } + lua_rawset(L, -3); + } + h = get_charinfo_vert_variants(co); + if (h != NULL) { + int i = 1; + lua_push_string_by_name(L,vert_variants); + lua_newtable(L); + while (h != NULL) { + lua_createtable(L, 0, 5); + dump_intfield(L, glyph, h->glyph); + dump_intfield(L, extender, h->extender); + dump_intfield(L, start, h->start_overlap); + dump_intfield(L, end, h->end_overlap); + dump_intfield(L, advance, h->advance); + lua_rawseti(L, -2, i); + i++; + h = h->next; + } + lua_rawset(L, -3); + } + } + ki = get_charinfo_kerns(co); + if (ki != NULL) { + int i; + lua_push_string_by_name(L,kerns); + lua_createtable(L, 10, 1); + for (i = 0; !kern_end(ki[i]); i++) { + if (kern_disabled(ki[i])) { + /* skip like in lookup */ + } else { + lua_rawgeti(L, -1, kern_char(ki[i])); + if (lua_type(L,-1) == LUA_TNIL) { + lua_pop(L,1); + if (kern_char(ki[i]) == right_boundarychar) { + lua_push_string_by_name(L,right_boundary); + } else { + lua_pushinteger(L, kern_char(ki[i])); + } + lua_pushinteger(L, kern_kern(ki[i])); + lua_rawset(L, -3); + } else { + /* first one wins */ + lua_pop(L,1); + } + } + } + lua_rawset(L, -3); + } + l = get_charinfo_ligatures(co); + if (l != NULL) { + int i; + lua_push_string_by_name(L,ligatures); + lua_createtable(L, 10, 1); + for (i = 0; !lig_end(l[i]); i++) { + if (lig_char(l[i]) == right_boundarychar) { + lua_push_string_by_name(L,right_boundary); + } else { + lua_pushinteger(L, lig_char(l[i])); + } + lua_createtable(L, 0, 2); + lua_push_string_by_name(L,type); + lua_pushinteger(L, lig_type(l[i])); + lua_rawset(L, -3); + lua_push_string_by_name(L,char); + lua_pushinteger(L, lig_replacement(l[i])); + lua_rawset(L, -3); + lua_rawset(L, -3); + } + lua_rawset(L, -3); + } + + lua_push_string_by_name(L,mathkern); + lua_newtable(L); + { + int i, j; + i = get_charinfo_math_kerns(co, top_right_kern); + j = 0; + if (i > 0) { + j++; + lua_push_string_by_name(L,top_right); + lua_newtable(L); + dump_math_kerns(L, co, i, top_right_kern); + lua_rawset(L, -3); + } + i = get_charinfo_math_kerns(co, top_left_kern); + if (i > 0) { + j++; + lua_push_string_by_name(L,top_left); + lua_newtable(L); + dump_math_kerns(L, co, i, top_left_kern); + lua_rawset(L, -3); + } + i = get_charinfo_math_kerns(co, bottom_right_kern); + if (i > 0) { + j++; + lua_push_string_by_name(L,bottom_right); + lua_newtable(L); + dump_math_kerns(L, co, i, bottom_right_kern); + lua_rawset(L, -3); + } + i = get_charinfo_math_kerns(co, bottom_left_kern); + if (i > 0) { + j++; + lua_push_string_by_name(L,bottom_left); + lua_newtable(L); + dump_math_kerns(L, co, i, bottom_left_kern); + lua_rawset(L, -3); + } + if (j > 0) + lua_rawset(L, -3); + else + lua_pop(L, 2); + } +} + +static void write_lua_parameters(lua_State * L, int f) +{ + int k; + lua_push_string_by_name(L,parameters); + lua_newtable(L); + for (k = 1; k <= font_params(f); k++) { + switch (k) { + case slant_code: + dump_intfield(L,slant,font_param(f, k)); + break; + case space_code: + dump_intfield(L,space,font_param(f, k)); + break; + case space_stretch_code: + dump_intfield(L,space_stretch,font_param(f, k)); + break; + case space_shrink_code: + dump_intfield(L,space_shrink,font_param(f, k)); + break; + case x_height_code: + dump_intfield(L,x_height,font_param(f, k)); + break; + case quad_code: + dump_intfield(L,quad,font_param(f, k)); + break; + case extra_space_code: + dump_intfield(L,extra_space,font_param(f, k)); + break; + default: + lua_pushinteger(L, font_param(f, k)); + lua_rawseti(L, -2, k); + } + } + lua_rawset(L, -3); +} + +@ @c +static void write_lua_math_parameters(lua_State * L, int f) +{ + int k; + lua_push_string_by_name(L,MathConstants); + lua_newtable(L); + for (k = 1; k <= font_math_params(f); k++) { + lua_pushinteger(L, font_math_param(f, k)); + if (k <= MATH_param_max) { + lua_setfield(L, -2, MATH_param_names[k]); + } else { + lua_rawseti(L, -2, k); + } + } + lua_rawset(L, -3); +} + +int font_to_lua(lua_State * L, int f) +{ + int k; + charinfo *co; + if (font_cache_id(f) > 0) { + /* fetch the table from the registry if it was + saved there by |font_from_lua()| */ + lua_rawgeti(L, LUA_REGISTRYINDEX, font_cache_id(f)); + /* fontdimens can be changed from tex code */ + write_lua_parameters(L, f); + return 1; + } + + lua_newtable(L); + lua_push_string_by_name(L,name); + lua_pushstring(L, font_name(f)); + lua_rawset(L, -3); + if (font_area(f) != NULL) { + dump_stringfield(L,area,font_area(f)); + } + if (font_filename(f) != NULL) { + dump_stringfield(L,filename,font_filename(f)); + } + if (font_fullname(f) != NULL) { + dump_stringfield(L,fullname,font_fullname(f)); + } + if (font_psname(f) != NULL) { + dump_stringfield(L,psname,font_psname(f)); + } + if (font_encodingname(f) != NULL) { + dump_stringfield(L,encodingname,font_encodingname(f)); + } + + dump_booleanfield(L,used,(font_used(f) ? true : false)); + dump_stringfield(L,type,font_type_strings[font_type(f)]); + dump_stringfield(L,format,font_format_strings[font_format(f)]); + dump_stringfield(L,writingmode,font_writingmode_strings[font_writingmode(f)]); + dump_stringfield(L,identity,font_identity_strings[font_identity(f)]); + dump_stringfield(L,embedding,font_embedding_strings[font_embedding(f)]); + + dump_intfield(L,units_per_em,font_units_per_em(f)); + dump_intfield(L,size,font_size(f)); + dump_intfield(L,designsize,font_dsize(f)); + dump_intfield(L,checksum,font_checksum(f)); + dump_intfield(L,slant,font_slant(f)); + dump_intfield(L,extend,font_extend(f)); + dump_intfield(L,direction,font_natural_dir(f)); + dump_intfield(L,encodingbytes,font_encodingbytes(f)); + dump_booleanfield(L,oldmath,font_oldmath(f)); + dump_intfield(L,tounicode,font_tounicode(f)); + + /* the next one is read only */ + if (font_max_shrink(f) != 0) { + dump_intfield(L,shrink,font_max_shrink(f)); + } + if (font_max_stretch(f) != 0) { + dump_intfield(L,stretch,font_max_stretch(f)); + } + if (font_step(f) != 0) { + dump_intfield(L,step,font_step(f)); + } + if (font_auto_expand(f) != 0) { + dump_booleanfield(L,auto_expand,font_auto_expand(f)); + } + if (pdf_font_attr(f) != 0) { + char *s = makecstring(pdf_font_attr(f)); + dump_stringfield(L,attributes,s); + free(s); + } + + /* params */ + write_lua_parameters(L, f); + write_lua_math_parameters(L, f); + + /* chars */ + lua_push_string_by_name(L,characters); + lua_createtable(L, font_tables[f]->charinfo_size, 0); /* all characters */ + if (has_left_boundary(f)) { + co = get_charinfo(f, left_boundarychar); + lua_push_string_by_name(L,left_boundary); + font_char_to_lua(L, f, co); + lua_rawset(L, -3); + } + if (has_right_boundary(f)) { + co = get_charinfo(f, right_boundarychar); + lua_push_string_by_name(L,right_boundary); + font_char_to_lua(L, f, co); + lua_rawset(L, -3); + } + for (k = font_bc(f); k <= font_ec(f); k++) { + if (quick_char_exists(f, k)) { + lua_pushinteger(L, k); + co = get_charinfo(f, k); + font_char_to_lua(L, f, co); + lua_rawset(L, -3); + } + } + lua_rawset(L, -3); + + if (font_cache_id(f) == 0) { /* renew */ + int r; + lua_pushvalue(L, -1); + r = luaL_ref(L, LUA_REGISTRYINDEX); /* pops the table */ + set_font_cache_id(f, r); + } + return 1; +} + +#define count_hash_items(L,name,n) \ + n = 0; \ + lua_key_rawgeti(name); \ + if (lua_type(L, -1) == LUA_TTABLE) { \ + lua_pushnil(L); \ + while (lua_next(L, -2) != 0) { \ + n++; \ + lua_pop(L, 1); \ + } \ + } \ + if (n) { \ + /* keep table on stack */ \ + } else{ \ + lua_pop(L, 1); \ + } + +@ @c +#define streq(a,b) (strcmp(a,b)==0) + +#define append_packet(k) { *(cp++) = (eight_bits) (k); } + +#define do_store_four(l) { \ + append_packet((l & 0xFF000000) >> 24); \ + append_packet((l & 0x00FF0000) >> 16); \ + append_packet((l & 0x0000FF00) >> 8); \ + append_packet((l & 0x000000FF)); \ +} + +@ @c +static void append_float(eight_bits ** cpp, float a) +{ + unsigned int i; + eight_bits *cp = *cpp; + union U { + float a; + eight_bits b[sizeof(float)]; + } u; + u.a = a; + for (i = 0; i < sizeof(float); i++) + append_packet(u.b[i]); + *cpp = cp; +} + +static int n_enum_field(lua_State * L, int name_index, int dflt, const char **values) +{ + int k, t; + const char *s; + int i = dflt; + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + t = lua_type(L,-1); + if (t == LUA_TNUMBER) { + i = (int) lua_tointeger(L, -1); + } else if (t == LUA_TSTRING) { + s = lua_tostring(L, -1); + k = 0; + while (values[k] != NULL) { + if (strcmp(values[k], s) == 0) { + i = k; + break; + } + k++; + } + } + lua_pop(L, 1); + return i; +} + +static int n_boolean_field(lua_State * L, int name_index, int dflt) +{ + int i = dflt; + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + if (lua_isboolean(L, -1)) { + i = lua_toboolean(L, -1); + } + lua_pop(L, 1); + return i; +} + +static char *n_string_field_copy(lua_State * L, int name_index, const char *dflt) +{ + char *i; + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + if (lua_type(L,-1) == LUA_TSTRING) { + i = xstrdup(lua_tostring(L, -1)); + } else if (dflt == NULL) { + i = NULL; + } else { + i = xstrdup(dflt); + } + lua_pop(L, 1); + return i; +} + +static const char *n_string_field(lua_State * L, int name_index) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + return lua_tostring(L,-1); +} + +static int n_some_field(lua_State * L, int name_index) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + return lua_type(L,-1); +} + +/*static void init_font_string_pointers(lua_State * L){}*/ + +static int count_char_packet_bytes(lua_State * L) +{ + register int i; + register int l = 0; + int ff = 0; + for (i = 1; i <= (int) lua_rawlen(L, -1); i++) { + lua_rawgeti(L, -1, i); + if (lua_istable(L, -1)) { + lua_rawgeti(L, -1, 1); + if (lua_type(L,-1) == LUA_TSTRING) { + const char *s = lua_tostring(L, -1); + if (lua_key_eq(s, font)) { + l += 5; + ff = 1; + } else if (lua_key_eq(s, char)) { + if (ff == 0) { + l += 5; + } + l += 5; + ff = 1; + } else if (lua_key_eq(s, slot)) { + l += 10; + ff = 1; + } else if (lua_key_eq(s, comment) || lua_key_eq(s, nop)) { + ; + } else if (lua_key_eq(s, push) || lua_key_eq(s, pop)) { + l++; + } else if (lua_key_eq(s, rule)) { + l += 9; + } else if (lua_key_eq(s, right) || lua_key_eq(s, node) + || lua_key_eq(s, down) || lua_key_eq(s, image)) { + l += 5; + } else if (lua_key_eq(s, scale)) { + l += sizeof(float) + 1; + } else if (lua_key_eq(s, special) || lua_key_eq(s, lua)) { + size_t len; + lua_rawgeti(L, -2, 2); + if (lua_type(L,-1) == LUA_TSTRING) { + (void) lua_tolstring(L, -1, &len); + lua_pop(L, 1); + if (len > 0) { + l = (int) (l + 5 + (int) len); + } + } else { + lua_pop(L, 1); + normal_error("vf command","invalid packet special"); + /* fprintf(stdout, "invalid packet special!\n"); */ + } + } else { + normal_error("vf command","unknown packet command"); + /* fprintf(stdout, "unknown packet command %s!\n", s); */ + } + } else { + normal_error("vf command","no packet command"); + /* fprintf(stdout, "no packet command!\n"); */ + } + lua_pop(L, 1); /* command name */ + } + lua_pop(L, 1); /* item */ + } + return l; +} + +static scaled sp_to_dvi(halfword sp, halfword atsize) +{ + double result, mult; + mult = (double) (atsize / 65536.0); + result = (double) (sp * 16.0); + return floor(result / mult); +} + +@ @c +static void read_char_packets(lua_State * L, int *l_fonts, charinfo * co, internal_font_number f, int atsize) +{ + int i, n, m; + size_t l; + int cmd; + const char *s; + eight_bits *cpackets, *cp; + int ff = 0; + int max_f = 0; + int pc = count_char_packet_bytes(L); + if (pc <= 0) + return; + while (l_fonts[(max_f + 1)] != 0) + max_f++; + cp = cpackets = xmalloc((unsigned) (pc + 1)); + for (i = 1; i <= (int) lua_rawlen(L, -1); i++) { + lua_rawgeti(L, -1, i); + if (lua_istable(L, -1)) { + /* fetch the command code */ + lua_rawgeti(L, -1, 1); + if (lua_type(L,-1) == LUA_TSTRING) { + s = lua_tostring(L, -1); + cmd = 0; + if (lua_key_eq(s, font)) { + cmd = packet_font_code; + } else if (lua_key_eq(s, char)) { + cmd = packet_char_code; + if (ff == 0) { + append_packet(packet_font_code); + ff = l_fonts[1]; + do_store_four(ff); + } + } else if (lua_key_eq(s, slot)) { + cmd = packet_nop_code; + lua_rawgeti(L, -2, 2); + n = (int) luaL_checkinteger(L, -1); + if (n ==0) { + ff = f; + } else { + ff = (n > max_f ? l_fonts[1] : l_fonts[n]); + } + lua_rawgeti(L, -3, 3); + n = (int) luaL_checkinteger(L, -1); + lua_pop(L, 2); + append_packet(packet_font_code); + do_store_four(ff); + append_packet(packet_char_code); + do_store_four(n); + } else if (lua_key_eq(s, comment) || lua_key_eq(s, nop)) { + cmd = packet_nop_code; + } else if (lua_key_eq(s, node)) { + cmd = packet_node_code; + } else if (lua_key_eq(s, push)) { + cmd = packet_push_code; + } else if (lua_key_eq(s, pop)) { + cmd = packet_pop_code; + } else if (lua_key_eq(s, rule)) { + cmd = packet_rule_code; + } else if (lua_key_eq(s, right)) { + cmd = packet_right_code; + } else if (lua_key_eq(s, down)) { + cmd = packet_down_code; + } else if (lua_key_eq(s, special)) { + cmd = packet_special_code; + } else if (lua_key_eq(s, image)) { + cmd = packet_image_code; + } else if (lua_key_eq(s, scale)) { + cmd = packet_scale_code; + } else if (lua_key_eq(s, lua)) { + cmd = packet_lua_code; + } + switch (cmd) { + case packet_push_code: + case packet_pop_code: + append_packet(cmd); + break; + case packet_font_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + n = (int) luaL_checkinteger(L, -1); + if (n == 0) { + ff = n; + } else { + ff = (n > max_f ? l_fonts[1] : l_fonts[n]); + } + do_store_four(ff); + lua_pop(L, 1); + break; + case packet_node_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + n = copy_node_list(nodelist_from_lua(L)); + do_store_four(n); + lua_pop(L, 1); + break; + case packet_char_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + n = (int) luaL_checkinteger(L, -1); + do_store_four(n); + lua_pop(L, 1); + break; + case packet_right_code: + case packet_down_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + n = (int) luaL_checkinteger(L, -1); + do_store_four(sp_to_dvi(n, atsize)); + lua_pop(L, 1); + break; + case packet_rule_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + n = (int) luaL_checkinteger(L, -1); + do_store_four(sp_to_dvi(n, atsize)); + lua_rawgeti(L, -3, 3); + n = (int) luaL_checkinteger(L, -1); + do_store_four(sp_to_dvi(n, atsize)); + lua_pop(L, 2); + break; + case packet_special_code: + case packet_lua_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + s = luaL_checklstring(L, -1, &l); + if (l > 0) { + do_store_four(l); + m = (int) l; + while (m > 0) { + n = *s++; + m--; + append_packet(n); + } + } + lua_pop(L, 1); + break; + case packet_image_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); /* img/imgtable? ... */ + if (lua_istable(L, -1)) { /* imgtable ... */ + lua_getglobal(L, "img"); /* imglib imgtable ... */ + lua_pushstring(L, "new"); /* `new' imglib imgtable ... */ + lua_gettable(L, -2); /* f imglib imgtable ... */ + lua_insert(L, -3); /* imglib imgtable f ... */ + lua_pop(L, 1); /* imgtable f ... */ + lua_call(L, 1, 1); + } /* img ... */ + luaL_checkudata(L, -1, TYPE_IMG); /* img ... --- just typecheck */ + n = luaL_ref(L, LUA_REGISTRYINDEX); /* ... */ + do_store_four(n); + break; + case packet_nop_code: + break; + case packet_scale_code: + append_packet(cmd); + lua_rawgeti(L, -2, 2); + append_float(&cp, (float) luaL_checknumber(L, -1)); + lua_pop(L, 1); + break; + default: + normal_error("vf command","invalid packet code"); + /* fprintf(stdout, "Unknown char packet code %s\n", s); */ + } + } + lua_pop(L, 1); /* command code */ + } else { + normal_error("vf command","commands has to be a tbale"); + /* fprintf(stdout, "Found a `commands' item that is not a table\n"); */ + } + lua_pop(L, 1); /* command table */ + } + append_packet(packet_end_code); + set_charinfo_packets(co, cpackets); + return; +} + +@ @c +static void read_lua_cidinfo(lua_State * L, int f) +{ + int i; + char *s; + /*lua_getfield(L, -1, "cidinfo");*/ + lua_key_rawgeti(cidinfo); + if (lua_istable(L, -1)) { + i = lua_numeric_field_by_index(L,lua_key_index(version), 0); + set_font_cidversion(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(supplement), 0); + set_font_cidsupplement(f, i); + s = n_string_field_copy(L, lua_key_index(registry), "Adobe"); /* Adobe-Identity-0 */ + set_font_cidregistry(f, s); + s = n_string_field_copy(L, lua_key_index(ordering), "Identity"); + set_font_cidordering(f, s); + } + lua_pop(L, 1); +} + + +@ @c +static void read_lua_parameters(lua_State * L, int f) +{ + int i, n, t; + const char *s; + /*lua_getfield(L, -1, "parameters");*/ + lua_key_rawgeti(parameters); + if (lua_istable(L, -1)) { + /* the number of parameters is the max(IntegerKeys(L)),7) */ + n = 7; + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + if (lua_type(L, -2) == LUA_TNUMBER) { + i = (int) lua_tointeger(L, -2); + if (i > n) + n = i; + } + lua_pop(L, 1); /* pop value */ + } + if (n > 7) + set_font_params(f, n); + /* sometimes it is handy to have all integer keys */ + for (i = 1; i <= 7; i++) { + lua_rawgeti(L, -1, i); + if (lua_type(L, -1) == LUA_TNUMBER) { + n = lua_roundnumber(L, -1); /* round ? */ + set_font_param(f, i, n); + } + lua_pop(L, 1); + } + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + t = lua_type(L,-2); + if (t == LUA_TNUMBER) { + i = (int) lua_tointeger(L, -2); + if (i >= 8) { + if (lua_type(L,-1) == LUA_TNUMBER) { + n = lua_roundnumber(L, -1); + } else { + n = 0; + } + set_font_param(f, i, n); + } + } else if (t == LUA_TSTRING) { + s = lua_tostring(L, -2); + if (lua_type(L,-1) == LUA_TNUMBER) { + n = lua_roundnumber(L, -1); + } else { + n = 0; + } + if (lua_key_eq(s, slant)) { + set_font_param(f, slant_code, n); + } else if (lua_key_eq(s, space)) { + set_font_param(f, space_code, n); + } else if (lua_key_eq(s, space_stretch)) { + set_font_param(f, space_stretch_code, n); + } else if (lua_key_eq(s, space_shrink)) { + set_font_param(f, space_shrink_code, n); + } else if (lua_key_eq(s, x_height)) { + set_font_param(f, x_height_code, n); + } else if (lua_key_eq(s, quad)) { + set_font_param(f, quad_code, n); + } else if (lua_key_eq(s, extra_space)) { + set_font_param(f, extra_space_code, n); + } + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); + +} + +@ @c +static void read_lua_math_parameters(lua_State * L, int f) +{ + int i = 0, n = 0, t; + /*lua_getfield(L, -1, "MathConstants");*/ + lua_key_rawgeti(MathConstants); + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + t = lua_type(L,-2); + if (t == LUA_TNUMBER) { + i = (int) lua_tointeger(L, -2); + } else if (t == LUA_TSTRING) { + i = ff_checkoption(L, -2, NULL, MATH_param_names); + } + n = (int) lua_roundnumber(L, -1); + if (i > 0) { + set_font_math_param(f, i, n); + } + lua_pop(L, 1); /* pop value */ + } + set_font_oldmath(f,false); + } else { + set_font_oldmath(f,true); + } + lua_pop(L, 1); +} + +@ @c +#define MIN_INF -0x7FFFFFFF + +static void store_math_kerns(lua_State * L, int index, charinfo * co, int id) +{ + int l, k; + scaled ht, krn; + lua_key_direct_rawgeti(index); + if (lua_istable(L, -1) && ((k = (int) lua_rawlen(L, -1)) > 0)) { + for (l = 0; l < k; l++) { + lua_rawgeti(L, -1, (l + 1)); + if (lua_istable(L, -1)) { + ht = (scaled) lua_numeric_field_by_index(L, lua_key_index(height), MIN_INF); + krn = (scaled) lua_numeric_field_by_index(L, lua_key_index(kern), MIN_INF); + if (krn > MIN_INF && ht > MIN_INF) + add_charinfo_math_kern(co, id, ht, krn); + } + lua_pop(L, 1); + } + } + lua_pop(L, 1); +} + +@ @c +static void font_char_from_lua(lua_State * L, internal_font_number f, int i, int *l_fonts, boolean has_math) +{ + int k, r, t, lt, u, n; + charinfo *co; + kerninfo *ckerns; + liginfo *cligs; + scaled j; + const char *s; + int nl = 0; /* number of ligature table items */ + int nk = 0; /* number of kern table items */ + int ctr = 0; + int atsize = font_size(f); + if (lua_istable(L, -1)) { + co = get_charinfo(f, i); + set_charinfo_tag(co, 0); + j = lua_numeric_field_by_index(L, lua_key_index(width), 0); + set_charinfo_width(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(height), 0); + set_charinfo_height(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(depth), 0); + set_charinfo_depth(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(italic), 0); + set_charinfo_italic(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(vert_italic), 0); + set_charinfo_vert_italic(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(index), 0); + set_charinfo_index(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(expansion_factor), 1000); + set_charinfo_ef(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(left_protruding), 0); + set_charinfo_lp(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(right_protruding), 0); + set_charinfo_rp(co, j); + k = n_boolean_field(L, lua_key_index(used), 0); + set_charinfo_used(co, k); + s = n_string_field(L, lua_key_index(name)); + if (s != NULL) + set_charinfo_name(co, xstrdup(s)); + else + set_charinfo_name(co, NULL); + /* n_string_field leaves a value on stack*/ + lua_pop(L,1); + u = n_some_field(L,lua_key_index(tounicode)); + if (u == LUA_TNUMBER) { + u = lua_tointeger(L,-1); + if (u < 0) { + set_charinfo_tounicode(co, NULL); + } else if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) { + char *s = malloc(5); + sprintf(s,"%04X",(unsigned int) u); + set_charinfo_tounicode(co,s); + } else { + char *s = malloc(9); + u = u - 0x10000; + sprintf(s,"%04X%04X",(unsigned int) (floor(u/1024)+0xD800),(unsigned int) (u%1024+0xDC00)); + set_charinfo_tounicode(co,s); + } + } else if (u == LUA_TTABLE) { + n = lua_rawlen(L,-1); + u = 0; + for (k = 1; k <= n; k++) { + lua_rawgeti(L, -1, k); + if (lua_type(L,-1) == LUA_TNUMBER) { + u = lua_tointeger(L,-1); + } else { + lua_pop(L, 1); + break; + } + if (u < 0) { + u = -1; + lua_pop(L, 1); + break; + } else if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) { + u = u + 4; + } else { + u = u + 8; + } + lua_pop(L, 1); + } + if (u>0) { + char *s = malloc(u+1); + char *t = s ; + for (k = 1; k <= n; k++) { + lua_rawgeti(L, -1, k); + u = lua_tointeger(L,-1); + if (u < 0xD7FF || (u > 0xDFFF && u <= 0xFFFF)) { + sprintf(t,"%04X",(unsigned int) u); + t += 4; + } else { + u = u - 0x10000; + sprintf(t,"%04X%04X",(unsigned int) (floor(u/1024)+0xD800),(unsigned int) (u%1024+0xDC00)); + t += 8; + } + lua_pop(L, 1); + } + set_charinfo_tounicode(co,s); + } else { + set_charinfo_tounicode(co, NULL); + } + } else if (u == LUA_TSTRING) { + s = lua_tostring(L,-1); + set_charinfo_tounicode(co, xstrdup(s)); + } else { + set_charinfo_tounicode(co, NULL); + } + /* ... leaves a value on stack*/ + lua_pop(L,1); + + if (has_math) { + j = lua_numeric_field_by_index(L, lua_key_index(top_accent), INT_MIN); + set_charinfo_top_accent(co, j); + j = lua_numeric_field_by_index(L, lua_key_index(bot_accent), INT_MIN); + set_charinfo_bot_accent(co, j); + k = lua_numeric_field_by_index(L, lua_key_index(next), -1); + if (k >= 0) { + set_charinfo_tag(co, list_tag); + set_charinfo_remainder(co, k); + } + + lua_key_rawgeti(extensible); + if (lua_istable(L, -1)) { + int top, bot, mid, rep; + top = lua_numeric_field_by_index(L, lua_key_index(top), 0); + bot = lua_numeric_field_by_index(L, lua_key_index(bot), 0); + mid = lua_numeric_field_by_index(L, lua_key_index(mid), 0); + rep = lua_numeric_field_by_index(L, lua_key_index(rep), 0); + if (top != 0 || bot != 0 || mid != 0 || rep != 0) { + set_charinfo_tag(co, ext_tag); + set_charinfo_extensible(co, top, bot, mid, rep); + } else { + formatted_warning("font", "lua-loaded font %s char U+%X has an invalid extensible field", + font_name(f), (int) i); + } + } + lua_pop(L, 1); + + lua_key_rawgeti(horiz_variants); + if (lua_istable(L, -1)) { + int glyph, startconnect, endconnect, advance, extender; + extinfo *h; + set_charinfo_tag(co, ext_tag); + set_charinfo_hor_variants(co, NULL); + for (k = 1;; k++) { + lua_rawgeti(L, -1, k); + if (lua_istable(L, -1)) { + glyph = lua_numeric_field_by_index(L, lua_key_index(glyph), 0); + extender = lua_numeric_field_by_index(L, lua_key_index(extender), 0); + startconnect = lua_numeric_field_by_index(L, lua_key_index(start), 0); + endconnect = lua_numeric_field_by_index(L, lua_key_index(end), 0); + advance = lua_numeric_field_by_index(L, lua_key_index(advance), 0); + h = new_variant(glyph, startconnect, endconnect, advance, extender); + add_charinfo_hor_variant(co, h); + lua_pop(L, 1); + } else { + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); + + lua_key_rawgeti(vert_variants); + if (lua_istable(L, -1)) { + int glyph, startconnect, endconnect, advance, extender; + extinfo *h; + set_charinfo_tag(co, ext_tag); + set_charinfo_vert_variants(co, NULL); + for (k = 1;; k++) { + lua_rawgeti(L, -1, k); + if (lua_istable(L, -1)) { + glyph = lua_numeric_field_by_index(L, lua_key_index(glyph), 0); + extender = lua_numeric_field_by_index(L, lua_key_index(extender), 0); + startconnect = lua_numeric_field_by_index(L, lua_key_index(start), 0); + endconnect = lua_numeric_field_by_index(L, lua_key_index(end), 0); + advance = lua_numeric_field_by_index(L, lua_key_index(advance), 0); + h = new_variant(glyph, startconnect, endconnect, advance, extender); + add_charinfo_vert_variant(co, h); + lua_pop(L, 1); + } else { + lua_pop(L, 1); + break; + } + } + } + lua_pop(L, 1); + + /* + Here is a complete example: + + |mathkern = {| + | bottom_left = { { height = 420, kern = 80 }, { height = 520, kern = 4 } },| + | bottom_right = { { height = 0, kern = 48 } },| + | top_left = { { height = 620, kern = 0 }, { height = 720, kern = -80 } },| + | top_right = { { height = 676, kern = 115 }, { height = 776, kern = 45 } },| + |}| + + */ + + lua_key_rawgeti(mathkern); + if (lua_istable(L, -1)) { + store_math_kerns(L,lua_key_index(top_left), co, top_left_kern); + store_math_kerns(L,lua_key_index(top_right), co, top_right_kern); + store_math_kerns(L,lua_key_index(bottom_right), co, bottom_right_kern); + store_math_kerns(L,lua_key_index(bottom_left), co, bottom_left_kern); + } + lua_pop(L, 1); + } + /* end of |has_math| */ + count_hash_items(L, kerns, nk); + if (nk > 0) { + /* kerns table still on stack */ + ckerns = xcalloc((unsigned) (nk + 1), sizeof(kerninfo)); + ctr = 0; + lua_pushnil(L); /* traverse hash */ + while (lua_next(L, -2) != 0) { + k = non_boundarychar; + lt = lua_type(L,-2); + if (lt == LUA_TNUMBER) { + k = (int) lua_tointeger(L, -2); /* adjacent char */ + if (k < 0) + k = non_boundarychar; + } else if (lt == LUA_TSTRING) { + s = lua_tostring(L, -2); + if (lua_key_eq(s, right_boundary)) { + k = right_boundarychar; + if (!has_right_boundary(f)) + set_right_boundary(f, get_charinfo(f, right_boundarychar)); + } + } + j = lua_roundnumber(L, -1); /* movement */ + if (k != non_boundarychar) { + set_kern_item(ckerns[ctr], k, j); + ctr++; + } else { + formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kern field", + font_name(f), (int) i); + } + lua_pop(L, 1); + } + /* guard against empty tables */ + if (ctr > 0) { + set_kern_item(ckerns[ctr], end_kern, 0); + set_charinfo_kerns(co, ckerns); + } else { + formatted_warning("font", "lua-loaded font %s char U+%X has an invalid kerns field", + font_name(f), (int) i); + } + lua_pop(L, 1); + } + + /* packet commands */ + lua_key_rawgeti(commands); + if (lua_istable(L, -1)) { + lua_pushnil(L); /* first key */ + if (lua_next(L, -2) != 0) { + lua_pop(L, 2); + read_char_packets(L, (int *) l_fonts, co, f, atsize); + } + } + lua_pop(L, 1); + + /* ligatures */ + count_hash_items(L, ligatures, nl); + if (nl > 0) { + /* ligatures table still on stack */ + cligs = xcalloc((unsigned) (nl + 1), sizeof(liginfo)); + ctr = 0; + lua_pushnil(L); /* traverse hash */ + while (lua_next(L, -2) != 0) { + k = non_boundarychar; + lt = lua_type(L,-2); + if (lt == LUA_TNUMBER) { + k = (int) lua_tointeger(L, -2); /* adjacent char */ + if (k < 0) { + k = non_boundarychar; + } + } else if (lt == LUA_TSTRING) { + s = lua_tostring(L, -2); + if (lua_key_eq(s, right_boundary)) { + k = right_boundarychar; + if (!has_right_boundary(f)) + set_right_boundary(f, get_charinfo(f, right_boundarychar)); + } + } + r = -1; + if (lua_istable(L, -1)) { + r = lua_numeric_field_by_index(L, lua_key_index(char), -1); /* ligature */ + } + if (r != -1 && k != non_boundarychar) { + t = n_enum_field(L, lua_key_index(type), 0, ligature_type_strings); + set_ligature_item(cligs[ctr], (char) ((t * 2) + 1), k, r); + ctr++; + } else { + formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligature field", + font_name(f), (int) i); + } + lua_pop(L, 1); /* iterator value */ + } + /* guard against empty tables */ + if (ctr > 0) { + set_ligature_item(cligs[ctr], 0, end_ligature, 0); + set_charinfo_ligatures(co, cligs); + } else { + formatted_warning("font", "lua-loaded font %s char U+%X has an invalid ligatures field", + font_name(f), (int) i); + } + lua_pop(L, 1); /* ligatures table */ + } + } +} + +@ The caller has to fix the state of the lua stack when there is an error! + +@c +int font_from_lua(lua_State * L, int f) +{ + int i, n, r, t, lt; + int s_top; /* lua stack top */ + int bc; /* first char index */ + int ec; /* last char index */ + char *s; + const char *ss; + int *l_fonts = NULL; + int save_ref ; + boolean no_math = false; + + /* will we save a cache of the luat table? */ + + save_ref = 1; /* we start with ss = "yes" */ + ss = NULL; + ss = n_string_field(L, lua_key_index(cache)); + if (lua_key_eq(ss, no)) + save_ref = -1; + else if (lua_key_eq(ss, renew)) + save_ref = 0; + /* n_string_field leaves a value on stack*/ + lua_pop(L,1); + + /* the table is at stack index -1 */ + + s = n_string_field_copy(L,lua_key_index(area), ""); + set_font_area(f, s); + s = n_string_field_copy(L, lua_key_index(filename), NULL); + set_font_filename(f, s); + s = n_string_field_copy(L, lua_key_index(encodingname), NULL); + set_font_encodingname(f, s); + + s = n_string_field_copy(L, lua_key_index(name), NULL); + set_font_name(f, s); + s = n_string_field_copy(L, lua_key_index(fullname), font_name(f)); + set_font_fullname(f, s); + + if (s == NULL) { + formatted_error("font","lua-loaded font '%d' has no name!", f); + return false; + } + s = n_string_field_copy(L, lua_key_index(psname), NULL); + set_font_psname(f, s); + + i = lua_numeric_field_by_index(L,lua_key_index(units_per_em), 0); + set_font_units_per_em(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(designsize), 655360); + set_font_dsize(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(size), font_dsize(f)); + set_font_size(f, i); + set_font_checksum(f, (unsigned)(lua_unsigned_numeric_field_by_index(L,lua_key_index(checksum), 0))) ; + i = lua_numeric_field_by_index(L,lua_key_index(direction), 0); + set_font_natural_dir(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(encodingbytes), 0); + set_font_encodingbytes(f, (char) i); + i = n_boolean_field(L,lua_key_index(oldmath), 0); + set_font_oldmath(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(tounicode), 0); + set_font_tounicode(f, (char) i); + + i = lua_numeric_field_by_index(L,lua_key_index(extend), 1000); + if (i < FONT_EXTEND_MIN) + i = FONT_EXTEND_MIN; + if (i > FONT_EXTEND_MAX) + i = FONT_EXTEND_MAX; + set_font_extend(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(slant), 0); + if (i < FONT_SLANT_MIN) + i = FONT_SLANT_MIN; + if (i > FONT_SLANT_MAX) + i = FONT_SLANT_MAX; + set_font_slant(f, i); + + i = lua_numeric_field_by_index(L,lua_key_index(hyphenchar), default_hyphen_char_par); + set_hyphen_char(f, i); + i = lua_numeric_field_by_index(L,lua_key_index(skewchar), default_skew_char_par); + set_skew_char(f, i); + i = n_boolean_field(L, lua_key_index(used), 0); + set_font_used(f, (char) i); + + s = n_string_field_copy(L, lua_key_index(attributes), NULL); + if (s != NULL && strlen(s) > 0) { + i = maketexstring(s); + set_pdf_font_attr(f, i); + } + free(s); + + i = n_enum_field(L, lua_key_index(type), unknown_font_type, font_type_strings); + set_font_type(f, i); + i = n_enum_field(L, lua_key_index(format), unknown_format, font_format_strings); + set_font_format(f, i); + i = n_enum_field(L, lua_key_index(writingmode), unknown_writingmode, font_writingmode_strings); + set_font_writingmode(f, i); + i = n_enum_field(L, lua_key_index(identity), unknown_identity, font_identity_strings); + set_font_identity(f, i); + i = n_enum_field(L, lua_key_index(embedding), unknown_embedding, font_embedding_strings); + set_font_embedding(f, i); + if (font_encodingbytes(f) == 0 && (font_format(f) == opentype_format || font_format(f) == truetype_format)) { + set_font_encodingbytes(f, 2); + } + + /* now fetch the base fonts, if needed */ + count_hash_items(L, fonts, n); + if (n > 0) { + /* font table still on stack */ + l_fonts = xmalloc((unsigned) ((unsigned) (n + 2) * sizeof(int))); + memset(l_fonts, 0, (size_t) ((unsigned) (n + 2) * sizeof(int))); + for (i = 1; i <= n; i++) { + lua_rawgeti(L, -1, i); + if (lua_istable(L, -1)) { + lua_key_rawgeti(id); + if (lua_isnumber(L, -1)) { + l_fonts[i] = (int) lua_tointeger(L, -1); + if (l_fonts[i] == 0) { + l_fonts[i] = (int) f; + } + lua_pop(L, 2); /* pop id and entry */ + continue; + } + lua_pop(L, 1); /* pop id */ + }; + ss = NULL; + if (lua_istable(L, -1)) { + ss = n_string_field(L, lua_key_index(name)); + /* string is anchored */ + lua_pop(L,1); + } + if (ss != NULL) { + t = lua_numeric_field_by_index(L, lua_key_index(size), -1000); + /* TODO: the stack is messed up, otherwise this explicit resizing would not be needed */ + s_top = lua_gettop(L); + if (strcmp(font_name(f), ss) == 0) + l_fonts[i] = f; + else + l_fonts[i] = find_font_id(ss, t); + lua_settop(L, s_top); + } else { + formatted_error("font","invalid local font in lua-loaded font '%s'", font_name(f)); + } + lua_pop(L, 1); /* pop list entry */ + } + lua_pop(L, 1); /* pop font table */ + } else if (font_type(f) == virtual_font_type) { + formatted_error("font","invalid local fonts in lua-loaded font '%s'", font_name(f)); + } else { + l_fonts = xmalloc(3 * sizeof(int)); + l_fonts[0] = 0; + l_fonts[1] = f; + l_fonts[2] = 0; + } + + /* parameters */ + no_math = n_boolean_field(L, lua_key_index(nomath), 0); + read_lua_parameters(L, f); + if (!no_math) { + read_lua_math_parameters(L, f); + if (n_boolean_field(L, lua_key_index(oldmath), 0)) { + set_font_oldmath(f,true); + } + + } else { + set_font_oldmath(f,true); + } + read_lua_cidinfo(L, f); + + /* characters */ + lua_key_rawgeti(characters); + if (lua_istable(L, -1)) { + /* find the array size values */ + int num = 0; /* number of charinfo's to add */ + ec = 0; + bc = -1; + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + if (lua_isnumber(L, -2)) { + i = (int) lua_tointeger(L, -2); + if (i >= 0) { + if (lua_istable(L, -1)) { + num++; + if (i > ec) + ec = i; + if (bc < 0) + bc = i; + if (bc >= 0 && i < bc) + bc = i; + } + } + } + lua_pop(L, 1); + } + if (bc != -1) { + font_malloc_charinfo(f, num); + set_font_bc(f, bc); + set_font_ec(f, ec); + lua_pushnil(L); /* first key */ + while (lua_next(L, -2) != 0) { + lt = lua_type(L,-2); + if (lt == LUA_TNUMBER) { + i = (int) lua_tointeger(L, -2); + if (i >= 0) { + font_char_from_lua(L, f, i, l_fonts, !no_math); + } + } else if (lt == LUA_TSTRING) { + const char *ss1 = lua_tostring(L, -2); + if (lua_key_eq(ss1, left_boundary)) { + font_char_from_lua(L, f, left_boundarychar, l_fonts, + !no_math); + } else if (lua_key_eq(ss1, right_boundary)) { + font_char_from_lua(L, f, right_boundarychar, l_fonts, + !no_math); + } + } + lua_pop(L, 1); + } + lua_pop(L, 1); + + /* + Handle font expansion last: the |copy_font| routine is called eventually, + and that needs to know |bc| and |ec|. + */ + + if (font_type(f) != virtual_font_type) { + int fstep = lua_numeric_field_by_index(L, lua_key_index(step), 0); + if (fstep < 0) + fstep = 0; + if (fstep > 100) + fstep = 100; + if (fstep != 0) { + int fshrink = lua_numeric_field_by_index(L, lua_key_index(shrink), 0); + int fstretch =lua_numeric_field_by_index(L, lua_key_index(stretch), 0); + int fexpand = n_boolean_field(L, lua_key_index(auto_expand), 0); + if (fshrink < 0) + fshrink = 0; + if (fshrink > 500) + fshrink = 500; + fshrink -= (fshrink % fstep); + if (fshrink < 0) + fshrink = 0; + if (fstretch < 0) + fstretch = 0; + if (fstretch > 1000) + fstretch = 1000; + fstretch -= (fstretch % fstep); + if (fstretch < 0) + fstretch = 0; + set_expand_params(f, fexpand, fstretch, fshrink, fstep); + } + } + + } else { + /* jikes, no characters */ + formatted_warning("font","lua-loaded font '%d' with name '%s' has no characters", f, font_name(f)); + } + + if (save_ref > 0) { + r = luaL_ref(L, LUA_REGISTRYINDEX); /* pops the table */ + set_font_cache_id(f, r); + } else { + lua_pop(L, 1); + set_font_cache_id(f, save_ref); + } + } else { + /* jikes, no characters */ + formatted_warning("font","lua-loaded font '%d' with name '%s' has no character table", f, font_name(f)); + } + if (l_fonts != NULL) + free(l_fonts); + return true; +} + +@* Ligaturing. + +@c +static void nesting_append(halfword nest1, halfword newn) +{ + halfword tail = tlink(nest1); + if (tail == null) { + couple_nodes(nest1, newn); + } else { + couple_nodes(tail, newn); + } + tlink(nest1) = newn; +} + +static void nesting_prepend(halfword nest1, halfword newn) +{ + halfword head = vlink(nest1); + couple_nodes(nest1, newn); + if (head == null) { + tlink(nest1) = newn; + } else { + couple_nodes(newn, head); + } +} + +static void nesting_prepend_list(halfword nest1, halfword newn) +{ + halfword head = vlink(nest1); + couple_nodes(nest1, newn); + if (head == null) { + tlink(nest1) = tail_of_list(newn); + } else { + halfword tail = tail_of_list(newn); + couple_nodes(tail, head); + } +} + +static int test_ligature(liginfo * lig, halfword left, halfword right) +{ + if (type(left) != glyph_node) + return 0; + if (font(left) != font(right)) + return 0; + if (is_ghost(left) || is_ghost(right)) + return 0; + *lig = get_ligature(font(left), character(left), character(right)); + if (is_valid_ligature(*lig)) { + return 1; + } + return 0; +} + +static int try_ligature(halfword * frst, halfword fwd) +{ + halfword cur = *frst; + liginfo lig; + if (test_ligature(&lig, cur, fwd)) { + int move_after = (lig_type(lig) & 0x0C) >> 2; + int keep_right = ((lig_type(lig) & 0x01) != 0); + int keep_left = ((lig_type(lig) & 0x02) != 0); + halfword newgl = raw_glyph_node(); + font(newgl) = font(cur); + character(newgl) = lig_replacement(lig); + set_is_ligature(newgl); + /* + below might not be correct in contrived border case. + but we use it only for debugging, so ... + */ + if (character(cur) < 0) { + set_is_leftboundary(newgl); + } + if (character(fwd) < 0) { + set_is_rightboundary(newgl); + } + if (character(cur) < 0) { + if (character(fwd) < 0) { + build_attribute_list(newgl); + } else { + add_node_attr_ref(node_attr(fwd)); + node_attr(newgl) = node_attr(fwd); + } + } else { + add_node_attr_ref(node_attr(cur)); + node_attr(newgl) = node_attr(cur); + } + + /* + TODO/FIXME if this ligature is consists of another ligature + we should add it's |lig_ptr| to the new glyphs |lig_ptr| (and + cleanup the no longer needed node) LOW PRIORITY + */ + /* left side */ + if (keep_left) { + halfword new_first = copy_node(cur); + lig_ptr(newgl) = new_first; + couple_nodes(cur, newgl); + if (move_after) { + move_after--; + cur = newgl; + } + } else { + halfword prev = alink(cur); + uncouple_node(cur); + lig_ptr(newgl) = cur; + couple_nodes(prev, newgl); + cur = newgl; /* as cur has disappeared */ + } + /* right side */ + if (keep_right) { + halfword new_second = copy_node(fwd); + /* correct, because we {\it know\/} |lig_ptr| points to {\it one\/} node */ + couple_nodes(lig_ptr(newgl), new_second); + couple_nodes(newgl, fwd); + if (move_after) { + move_after--; + cur = fwd; + } + } else { + halfword next = vlink(fwd); + uncouple_node(fwd); + /* correct, because we {\it know\/} |lig_ptr| points to {\it one\/} node */ + couple_nodes(lig_ptr(newgl), fwd); + if (next != null) { + couple_nodes(newgl, next); + } + } + /* check and return */ + *frst = cur; + return 1; + } + return 0; +} + +@ there shouldn't be any ligatures here - we only add them at the end of + |xxx_break| in a \.{DISC-1 - DISC-2} situation and we stop processing \.{DISC-1} + (we continue with \.{DISC-1}'s |post_| and |no_break|. + +@c +static halfword handle_lig_nest(halfword root, halfword cur) +{ + if (cur == null) + return root; + while (vlink(cur) != null) { + halfword fwd = vlink(cur); + if (type(cur) == glyph_node && type(fwd) == glyph_node && + font(cur) == font(fwd) && try_ligature(&cur, fwd)) { + continue; + } + cur = vlink(cur); + } + tlink(root) = cur; + return root; +} + +static halfword handle_lig_word(halfword cur) +{ + halfword right = null; + if (type(cur) == boundary_node) { + halfword prev = alink(cur); + halfword fwd = vlink(cur); + /* no need to uncouple |cur|, it is freed */ + flush_node(cur); + if (fwd == null) { + vlink(prev) = fwd; + return prev; + } + couple_nodes(prev, fwd); + if (type(fwd) != glyph_node) + return prev; + cur = fwd; + } else if (has_left_boundary(font(cur))) { + halfword prev = alink(cur); + halfword p = new_glyph(font(cur), left_boundarychar); + couple_nodes(prev, p); + couple_nodes(p, cur); + cur = p; + } + if (has_right_boundary(font(cur))) { + right = new_glyph(font(cur), right_boundarychar); + } + while (1) { + /* A glyph followed by ... */ + if (type(cur) == glyph_node) { + halfword fwd = vlink(cur); + if (fwd == null) { /* last character of paragraph */ + if (right == null) + break; + /* \.{--\\par} prohibits use of |couple_nodes| here */ + try_couple_nodes(cur, right); + right = null; + continue; + } + if (type(fwd) == glyph_node) { /* |GLYPH - GLYPH| */ + if (font(cur) != font(fwd)) + break; + if (try_ligature(&cur, fwd)) + continue; + } else if (type(fwd) == disc_node) { /* |GLYPH - DISC| */ + + /* if \.{a{bx}{}{y}} and \.{a+b=>B} convert to \.{{Bx}{}{ay}} */ + halfword pre = vlink_pre_break(fwd); + halfword nob = vlink_no_break(fwd); + halfword next, tail; + liginfo lig; + /* Check on: a{b?}{?}{?} and a+b=>B : {B?}{?}{a?} */ + /* Check on: a{?}{?}{b?} and a+b=>B : {a?}{?}{B?} */ + if ((pre != null && type(pre) == glyph_node + && test_ligature(&lig, cur, pre)) + || (nob != null && type(nob) == glyph_node + && test_ligature(&lig, cur, nob))) { + /* move cur from before disc, to skipped part */ + halfword prev = alink(cur); + uncouple_node(cur); + couple_nodes(prev, fwd); + nesting_prepend(no_break(fwd), cur); + /* now ligature the |pre_break| */ + nesting_prepend(pre_break(fwd), copy_node(cur)); + /* As we have removed cur, we need to start again ... */ + cur = prev; + } + /* Check on: a{?}{?}{}b and a+b=>B : {a?}{?b}{B} */ + next = vlink(fwd); + if (nob == null && next != null && type(next) == glyph_node + && test_ligature(&lig, cur, next)) { + /* move |cur| from before |disc| to |no_break| part */ + halfword prev = alink(cur); + uncouple_node(cur); + couple_nodes(prev, fwd); + couple_nodes(no_break(fwd), cur); /* we {\it know\/} it's empty */ + /* now copy cur the |pre_break| */ + nesting_prepend(pre_break(fwd), copy_node(cur)); + /* move next from after disc to |no_break| part */ + tail = vlink(next); + uncouple_node(next); + try_couple_nodes(fwd, tail); + couple_nodes(cur, next); /* we {\it know\/} this works */ + tlink(no_break(fwd)) = next; /* and make sure the list is correct */ + /* now copy next to the |post_break| */ + nesting_append(post_break(fwd), copy_node(next)); + /* As we have removed cur, we need to start again ... */ + cur = prev; + } + /* we are finished with the |pre_break| */ + handle_lig_nest(pre_break(fwd), vlink_pre_break(fwd)); + } else if (type(fwd) == boundary_node) { + halfword next = vlink(fwd); + try_couple_nodes(cur, next); + flush_node(fwd); + if (right != null) { + flush_node(right); /* Shame, didn't need it */ + /* no need to reset |right|, we're going to leave the loop anyway */ + } + break; + } else { + /* fwd is something unknown */ + if (right == null) + break; + couple_nodes(cur, right); + couple_nodes(right, fwd); + right = null; + continue; + } + /* A discretionary followed by ... */ + } else if (type(cur) == disc_node) { + /* If \.{{?}{x}{?}} or \.{{?}{?}{y}} then ... */ + if (vlink_no_break(cur) != null || vlink_post_break(cur) != null) { + halfword prev = 0; + halfword fwd; + liginfo lig; + if (subtype(cur) == select_disc) { + prev = alink(cur); + if (vlink_post_break(cur) != null) + handle_lig_nest(post_break(prev), vlink_post_break(prev)); + if (vlink_no_break(cur) != null) + handle_lig_nest(no_break(prev), vlink_no_break(prev)); + } + if (vlink_post_break(cur) != null) + handle_lig_nest(post_break(cur), vlink_post_break(cur)); + if (vlink_no_break(cur) != null) + handle_lig_nest(no_break(cur), vlink_no_break(cur)); + while ((fwd = vlink(cur)) != null) { + halfword nob, pst, next; + if (type(fwd) != glyph_node) + break; + if (subtype(cur) != select_disc) { + nob = tlink_no_break(cur); + pst = tlink_post_break(cur); + if ((nob == null || !test_ligature(&lig, nob, fwd)) && + (pst == null || !test_ligature(&lig, pst, fwd))) + break; + nesting_append(no_break(cur), copy_node(fwd)); + handle_lig_nest(no_break(cur), nob); + } else { + int dobreak = 0; + nob = tlink_no_break(prev); + pst = tlink_post_break(prev); + if ((nob == null || !test_ligature(&lig, nob, fwd)) && + (pst == null || !test_ligature(&lig, pst, fwd))) + dobreak = 1; + if (!dobreak) { + nesting_append(no_break(prev), copy_node(fwd)); + handle_lig_nest(no_break(prev), nob); + nesting_append(post_break(prev), copy_node(fwd)); + handle_lig_nest(post_break(prev), pst); + } + dobreak = 0; + nob = tlink_no_break(cur); + pst = tlink_post_break(cur); + if ((nob == null || !test_ligature(&lig, nob, fwd)) && + (pst == null || !test_ligature(&lig, pst, fwd))) + dobreak = 1; + if (!dobreak) { + nesting_append(no_break(cur), copy_node(fwd)); + handle_lig_nest(no_break(cur), nob); + } + if (dobreak) + break; + } + next = vlink(fwd); + uncouple_node(fwd); + try_couple_nodes(cur, next); + nesting_append(post_break(cur), fwd); + handle_lig_nest(post_break(cur), pst); + } + if (fwd != null && type(fwd) == disc_node) { + halfword next = vlink(fwd); + if (vlink_no_break(fwd) == null + && vlink_post_break(fwd) == null + && next != null + && type(next) == glyph_node + && ((tlink_post_break(cur) != null && test_ligature(&lig, tlink_post_break(cur), next)) || + (tlink_no_break (cur) != null && test_ligature(&lig, tlink_no_break (cur), next)))) { + /* Building an |init_disc| followed by a |select_disc| + \.{{a-}{b}{AB} {-}{}{}} 'c' + */ + /* is it tail necessary ? */ + halfword last1 = vlink(next), tail ; + uncouple_node(next); + try_couple_nodes(fwd, last1); + /* \.{{a-}{b}{AB} {-}{c}{}} */ + nesting_append(post_break(fwd), copy_node(next)); + /* \.{{a-}{b}{AB} {-}{c}{-}} */ + if (vlink_no_break(cur) != null) { + nesting_prepend(no_break(fwd), copy_node(vlink_pre_break(fwd))); + } + /* \.{{a-}{b}{AB} {b-}{c}{-}} */ + if (vlink_post_break(cur) != null) + nesting_prepend_list(pre_break(fwd), copy_node_list(vlink_post_break(cur))); + /* \.{{a-}{b}{AB} {b-}{c}{AB-}} */ + if (vlink_no_break(cur) != null) { + nesting_prepend_list(no_break(fwd), copy_node_list(vlink_no_break(cur))); + } + /* \.{{a-}{b}{ABC} {b-}{c}{AB-}} */ + tail = tlink_no_break(cur); + nesting_append(no_break(cur), copy_node(next)); + handle_lig_nest(no_break(cur), tail); + /* \.{{a-}{BC}{ABC} {b-}{c}{AB-}} */ + tail = tlink_post_break(cur); + nesting_append(post_break(cur), next); + handle_lig_nest(post_break(cur), tail); + /* and set the subtypes */ + subtype(cur) = init_disc; + subtype(fwd) = select_disc; + } + } + } + + } else { + /* NO GLYPH NOR DISC */ + return cur; + } + /* step-to-next-node */ + /* \.{--\\par} allows |vlink(cur)| to be null */ + cur = vlink(cur); + } + + return cur; +} + +@ Return value is the new tail, head should be a dummy + +@c +halfword handle_ligaturing(halfword head, halfword tail) +{ + halfword save_tail1 = null; /* trick to allow explicit |node==null| tests */ + halfword cur, prev; + + if (vlink(head) == null) + return tail; + if (tail != null) { + save_tail1 = vlink(tail); + vlink(tail) = null; + } + + /* |if (fix_node_lists)| */ + fix_node_list(head); + + prev = head; + cur = vlink(prev); + + while (cur != null) { + if (type(cur) == glyph_node || (type(cur) == boundary_node)) { + cur = handle_lig_word(cur); + } + prev = cur; + cur = vlink(cur); + } + if (prev == null) { + /* hh: looks bad to me */ + prev = tail; + } + + if (tail != null) { + try_couple_nodes(prev, save_tail1); + } + return prev; +} + + +@* Kerning. + +@c +static void add_kern_before(halfword left, halfword right) +{ + if ((!is_rightghost(right)) && + font(left) == font(right) && has_kern(font(left), character(left))) { + int k = raw_get_kern(font(left), character(left), character(right)); + if (k != 0) { + halfword kern = new_kern(k); + halfword prev = alink(right); + couple_nodes(prev, kern); + couple_nodes(kern, right); + /* update the attribute list (inherit from left) */ + delete_attribute_ref(node_attr(kern)); + add_node_attr_ref(node_attr(left)); + node_attr(kern) = node_attr(left); + } + } +} + +static void add_kern_after(halfword left, halfword right, halfword aft) +{ + if ((!is_rightghost(right)) && + font(left) == font(right) && has_kern(font(left), character(left))) { + int k = raw_get_kern(font(left), character(left), character(right)); + if (k != 0) { + halfword kern = new_kern(k); + halfword next = vlink(aft); + couple_nodes(aft, kern); + try_couple_nodes(kern, next); + /* update the attribute list (inherit from left == aft) */ + delete_attribute_ref(node_attr(kern)); + add_node_attr_ref(node_attr(aft)); + node_attr(kern) = node_attr(aft); + } + } +} + +static void do_handle_kerning(halfword root, halfword init_left, halfword init_right) +{ + halfword cur = vlink(root); + halfword left = null; + if (cur == null) { + if (init_left != null && init_right != null) { + add_kern_after(init_left, init_right, root); + tlink(root) = vlink(root); + } + return; + } + if (type(cur) == glyph_node) { + set_is_glyph(cur); + if (init_left != null) + add_kern_before(init_left, cur); + left = cur; + } + while ((cur = vlink(cur)) != null) { + if (type(cur) == glyph_node) { + set_is_glyph(cur); + if (left != null) { + add_kern_before(left, cur); + if (character(left) < 0 || is_ghost(left)) { + halfword prev = alink(left); + couple_nodes(prev, cur); + flush_node(left); + } + } + left = cur; + } else { + if (type(cur) == disc_node) { + halfword right = type(vlink(cur)) == glyph_node ? vlink(cur) : null; + do_handle_kerning(pre_break(cur), left, null); + if (vlink_pre_break(cur) != null) + tlink_pre_break(cur) = tail_of_list(vlink_pre_break(cur)); + do_handle_kerning(post_break(cur), null, right); + if (vlink_post_break(cur) != null) + tlink_post_break(cur) = tail_of_list(vlink_post_break(cur)); + do_handle_kerning(no_break(cur), left, right); + if (vlink_no_break(cur) != null) + tlink_no_break(cur) = tail_of_list(vlink_no_break(cur)); /* needed? */ + } + if (left != null) { + if (character(left) < 0 || is_ghost(left)) { + halfword prev = alink(left); + couple_nodes(prev, cur); + flush_node(left); + } + left = null; + } + } + } + if (left != null) { + if (init_right != null) + add_kern_after(left, init_right, left); + if (character(left) < 0 || is_ghost(left)) { + halfword prev = alink(left); + halfword next = vlink(left); + if (next != null) { + couple_nodes(prev, next); + tlink(root) = next; + } else if (prev != root) { + vlink(prev) = null; + tlink(root) = prev; + } else { + vlink(root) = null; + tlink(root) = null; + } + flush_node(left); + } + } +} + +/* +halfword handle_kerning(halfword head, halfword tail) +{ + halfword save_link; + save_link = vlink(tail); + vlink(tail) = null; + tlink(head) = tail; + do_handle_kerning(head, null, null); + tail = tlink(head); + if (valid_node(save_link)) { + try_couple_nodes(tail, save_link); + } + return tail; +} +*/ + +halfword handle_kerning(halfword head, halfword tail) +{ + halfword save_link = null; + if (tail == null) { + tlink(head) = null; + do_handle_kerning(head, null, null); + } else { + save_link = vlink(tail); + vlink(tail) = null; + tlink(head) = tail; + do_handle_kerning(head, null, null); + tail = tlink(head); + if (valid_node(save_link)) { + try_couple_nodes(tail, save_link); + } + } + return tail; +} + +@* ligaturing and kerning : lua-interface. + +@c +static halfword run_lua_ligkern_callback(halfword head, halfword tail, int callback_id) +{ + int i; + int top = lua_gettop(Luas); + if (!get_callback(Luas, callback_id)) { + lua_pop(Luas, 2); + return tail; + } + nodelist_to_lua(Luas, head); + nodelist_to_lua(Luas, tail); + if ((i=lua_pcall(Luas, 2, 0, 0)) != 0) { + luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + return tail; + } + fix_node_list(head); + lua_settop(Luas, top); + return tail; +} + +halfword new_ligkern(halfword head, halfword tail) +{ + int callback_id = 0; + if (vlink(head) == null) + return tail; + callback_id = callback_defined(ligaturing_callback); + if (callback_id > 0) { + tail = run_lua_ligkern_callback(head, tail, callback_id); + if (tail == null) + tail = tail_of_list(head); + } else if (callback_id == 0) { + tail = handle_ligaturing(head, tail); + } + callback_id = callback_defined(kerning_callback); + if (callback_id > 0) { + tail = run_lua_ligkern_callback(head, tail, callback_id); + if (tail == null) { + tail = tail_of_list(head); + } + } else if (callback_id == 0) { + halfword nest1 = new_node(nesting_node, 1); + halfword cur = vlink(head); + halfword aft = vlink(tail); + couple_nodes(nest1, cur); + tlink(nest1) = tail; + vlink(tail) = null; + do_handle_kerning(nest1, null, null); + couple_nodes(head, vlink(nest1)); + tail = tlink(nest1); + try_couple_nodes(tail, aft); + flush_node(nest1); + } + return tail; +} diff --git a/Build/source/texk/web2c/luatexdir/lang/texlang.w b/Build/source/texk/web2c/luatexdir/lang/texlang.w index cf88928e8d4..bd519af632a 100644 --- a/Build/source/texk/web2c/luatexdir/lang/texlang.w +++ b/Build/source/texk/web2c/luatexdir/lang/texlang.w @@ -321,13 +321,8 @@ void load_tex_hyphenation(int curlang, halfword head) load_hyphenation(get_language(curlang), (unsigned char *) s); } -@ TODO: clean this up. The |delete_attribute_ref()| statements are not very nice, -but needed. Also, in the post-break, it would be nicer to get the attribute list -from |vlink(n)|. No rush, as it is currently not used much. - -@c -halfword insert_discretionary(halfword t, halfword pre, halfword post, - halfword replace, int penalty) +@ @c +halfword insert_discretionary(halfword t, halfword pre, halfword post, halfword replace, int penalty) { halfword g, n; int f; @@ -417,24 +412,21 @@ halfword insert_syllable_discretionary(halfword t, lang_variables * lan) return n; } -halfword insert_word_discretionary(halfword t, lang_variables * lan) -{ - halfword pre = null, pos = null; - if (lan->pre_exhyphen_char > 0) - pre = insert_character(null, lan->pre_exhyphen_char); - if (lan->post_exhyphen_char > 0) - pos = insert_character(null, lan->post_exhyphen_char); - return insert_discretionary(t, pre, pos, null,ex_hyphen_penalty_par); -} - @ @c halfword compound_word_break(halfword t, int clang) { - int disc; - lang_variables langdata; - langdata.pre_exhyphen_char = get_pre_exhyphen_char(clang); - langdata.post_exhyphen_char = get_post_exhyphen_char(clang); - disc = insert_word_discretionary(t, &langdata); + halfword disc = null; + halfword pre = null; + halfword pos = null; + halfword pre_exhyphen_char = get_pre_exhyphen_char(clang); + halfword post_exhyphen_char = get_post_exhyphen_char(clang); + if (pre_exhyphen_char > 0) + pre = insert_character(null,pre_exhyphen_char); + if (post_exhyphen_char > 0) + pos = insert_character(null,post_exhyphen_char); + disc = insert_discretionary(t,pre,pos,null,ex_hyphen_penalty_par); + subtype(disc) = automatic_disc; + set_automatic_disc_penalty(disc); return disc; } @@ -443,7 +435,7 @@ halfword insert_complex_discretionary(halfword t, lang_variables * lan, halfword replace) { (void) lan; - return insert_discretionary(t, pre, pos, replace,hyphen_penalty_par); + return insert_discretionary(t,pre,pos,replace,hyphen_penalty_par); } halfword insert_character(halfword t, int c) @@ -775,8 +767,7 @@ static halfword find_next_wordstart(halfword r, halfword first_language, halfwor */ t = vlink(r) ; if ((start_ok == 0) && (t!=null) && (type(t) == glyph_node) && (character(t) != ex_hyphen_char_par)) { - t = compound_word_break(r, char_lang(r)); - subtype(t) = automatic_disc; + compound_word_break(r, char_lang(r)); start_ok = 1 ; } else { start_ok = 0; @@ -866,7 +857,7 @@ void hnj_hyphenation(halfword head, halfword tail) assert(tail != null); save_tail1 = vlink(tail); - s = new_penalty(0); + s = new_penalty(0,word_penalty); couple_nodes(tail, s); while (r != null) { /* could be while(1), but let's be paranoid */ @@ -947,15 +938,13 @@ void hnj_hyphenation(halfword head, halfword tail) set of explicit hyphens */ halfword rr = r; - halfword t = null; #ifdef VERBOSE formatted_warning("hyphenation","explicit hyphen(s) found in %s (c=%d)", utf8word, clang); #endif while (rr != wordstart) { if (is_simple_character(rr)) { if (character(rr) == ex_hyphen_char_par) { - t = compound_word_break(rr, clang); - subtype(t) = automatic_disc; + compound_word_break(rr, clang); while (character(alink(rr)) == ex_hyphen_char_par) rr = alink(rr); if (rr == wordstart) @@ -990,13 +979,13 @@ void hnj_hyphenation(halfword head, halfword tail) /* what is right overruns left .. a bit messy */ } /* maybe an extra check ... */ - /* if (left && right) { */ + /* |if (left && right) {| */ #ifdef VERBOSE formatted_warning("hyphenation","hyphenate %s (c=%d,l=%d,r=%d) from %c to %c", utf8word, clang, lhmin, rhmin, character(left), character(right)); #endif (void) hnj_hyphen_hyphenate(lang->patterns, wordstart, end_word, wordlen, left, right, &langdata); - /* } */ + /* |}| */ } } explicit_hyphen = false; diff --git a/Build/source/texk/web2c/luatexdir/lua/helpers.w b/Build/source/texk/web2c/luatexdir/lua/helpers.w new file mode 100644 index 00000000000..d12e476909d --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/lua/helpers.w @@ -0,0 +1,26 @@ +% helpers.w +% +% Copyright 2017 LuaTeX team +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ We will move here some helpers common to code that are now in weird places, +probably organized in texhelpers, luahelpers, pdfhelpers. + + + +@c +/* to fill... */ diff --git a/Build/source/texk/web2c/luatexdir/lua/lauxlib_bridge.h b/Build/source/texk/web2c/luatexdir/lua/lauxlib_bridge.h index 9f54ffe9103..d71a6786845 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lauxlib_bridge.h +++ b/Build/source/texk/web2c/luatexdir/lua/lauxlib_bridge.h @@ -23,10 +23,6 @@ /* http://lua.2524044.n2.nabble.com/converting-modules-in-written-in-C-for-Lua-5-1-to-Lua-5-2-td7642941.html */ #define luaL_setmetatable(L, tname) luaL_getmetatable(L, tname);lua_setmetatable(L, -2); -/* see http://comments.gmane.org/gmane.comp.programming.swig/18673 */ -/* -# define lua_rawlen lua_objlen -*/ LUALIB_API void *(luaL_testudata) (lua_State *L, int ud, const char *tname); @@ -39,23 +35,7 @@ LUA_API int lua_compare (lua_State *L, int index1, int index2, int op); #define lua_unlock(L) ((void) 0) #endif -#define luaL_newlibtable(L,l) \ - lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) - +#define luaL_newlibtable(L,l) lua_createtable(L, 0, sizeof(l)/sizeof((l)[0]) - 1) #define luaL_newlib(L,l) (luaL_newlibtable(L,l), luaL_setfuncs(L,l,0)) -/* Is this ok as replacement for lua_copy of lua 5.2.4 ? */ -/* -#if defined(LUAJIT) -LUA_API void lua_copy (lua_State *L, int fromidx, int toidx) { - TValue *fr, *to; - lua_lock(L); - fr = index2adr(L, fromidx); - to = index2adr(L, toidx); - api_checkvalidindex(L, to); - copyTV(L,to,fr); - lua_unlock(L); -} -#endif -*/ #endif diff --git a/Build/source/texk/web2c/luatexdir/lua/lepdflib.cc b/Build/source/texk/web2c/luatexdir/lua/lepdflib.cc index 00e27863a2c..7250ffd1863 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lepdflib.cc +++ b/Build/source/texk/web2c/luatexdir/lua/lepdflib.cc @@ -3484,11 +3484,6 @@ int luaopen_epdf(lua_State * L) setfuncs_meta(TextSpan); setfuncs_meta(XRef); setfuncs_meta(XRefEntry); - -#ifdef LuajitTeX luaL_register(L, "epdf", epdflib_f); -#else - luaL_newlib(L, epdflib_f); -#endif return 1; } diff --git a/Build/source/texk/web2c/luatexdir/lua/limglib.c b/Build/source/texk/web2c/luatexdir/lua/limglib.c index e82bf9d5184..6010bfd8500 100644 --- a/Build/source/texk/web2c/luatexdir/lua/limglib.c +++ b/Build/source/texk/web2c/luatexdir/lua/limglib.c @@ -761,12 +761,11 @@ int luaopen_img(lua_State * L) luaL_register(L, NULL, img_m); luaL_newmetatable(L, TYPE_IMG_DICT); luaL_register(L, NULL, img_dict_m); - luaL_register(L, "img", imglib_f); #else luaL_setfuncs(L, img_m, 0); luaL_newmetatable(L, TYPE_IMG_DICT); luaL_setfuncs(L, img_dict_m, 0); - luaL_newlib(L, imglib_f); #endif + luaL_register(L, "img", imglib_f); return 1; } diff --git a/Build/source/texk/web2c/luatexdir/lua/liolibext.c b/Build/source/texk/web2c/luatexdir/lua/liolibext.c index 630562228d9..e910139c2ce 100644 --- a/Build/source/texk/web2c/luatexdir/lua/liolibext.c +++ b/Build/source/texk/web2c/luatexdir/lua/liolibext.c @@ -15,18 +15,13 @@ License for more details. You should have received a copy of the GNU General Public License along - with LuaTeX; if not, see . */ + with LuaTeX; if not, see . -/* This extension only replaces one function: io.popen() and two - metatable entries: f:read() and f:lines() but unfortunately - it has to copy quite a bunch of lines from the original liolib.c - to do so. */ #include "ptexlib.h" #include "lua/luatex-api.h" - #ifdef LuajitTeX #include "lua/lauxlib_bridge.h" #else @@ -36,6 +31,10 @@ +#ifdef LuajitTeX +/* luajit has its own way for io, which is a mix of */ +/* lua 5.1 and lua 5.2 . We use the stock luajit. */ +#else /* ** {====================================================== ** lua_popen spawns a new process connected to the current @@ -46,17 +45,17 @@ #if defined(_WIN32) #ifdef _MSC_VER -#define lua_popen(L,c,m) ((void)L, win32_popen(c,m)) -#define lua_pclose(L,file) ((void)L, win32_pclose(file)) +#define lua_popen(L,c,m) ((void)L, win32_popen(c,m)) +#define lua_pclose(L,file) ((void)L, win32_pclose(file)) #else -#define lua_popen(L,c,m) ((void)L, _popen(c,m)) -#define lua_pclose(L,file) ((void)L, _pclose(file)) +#define lua_popen(L,c,m) ((void)L, _popen(c,m)) +#define lua_pclose(L,file) ((void)L, _pclose(file)) #endif #else -#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) -#define lua_pclose(L,file) ((void)L, pclose(file)) +#define lua_popen(L,c,m) ((void)L, fflush(NULL), popen(c,m)) +#define lua_pclose(L,file) ((void)L, pclose(file)) #endif @@ -65,17 +64,17 @@ #if defined(LUA_USE_POSIX) -#define l_fseek(f,o,w) fseeko(f,o,w) -#define l_ftell(f) ftello(f) -#define l_seeknum off_t +#define l_fseek(f,o,w) fseeko(f,o,w) +#define l_ftell(f) ftello(f) +#define l_seeknum off_t #elif defined(LUA_WIN) && !defined(_CRTIMP_TYPEINFO) \ && defined(_MSC_VER) && (_MSC_VER >= 1400) /* Windows (but not DDK) and Visual C++ 2005 or higher */ -#define l_fseek(f,o,w) _fseeki64(f,o,w) -#define l_ftell(f) _ftelli64(f) -#define l_seeknum __int64 +#define l_fseek(f,o,w) _fseeki64(f,o,w) +#define l_ftell(f) _ftelli64(f) +#define l_seeknum __int64 #elif defined(__MINGW32__) @@ -85,637 +84,371 @@ #else -#define l_fseek(f,o,w) fseek(f,o,w) -#define l_ftell(f) ftell(f) -#define l_seeknum long +#define l_fseek(f,o,w) fseek(f,o,w) +#define l_ftell(f) ftell(f) +#define l_seeknum long #endif -#define IO_PREFIX "_IO_" -#define IO_INPUT (IO_PREFIX "input") -#define IO_OUTPUT (IO_PREFIX "output") - -typedef luaL_Stream LStream; - -#define tolstream(L) ((LStream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)) - -#define isclosed(p) ((p)->closef == NULL) +#endif/* #ifdef LuajitTeX */ -static int io_type (lua_State *L) { - LStream *p; - luaL_checkany(L, 1); - p = (LStream *)luaL_testudata(L, 1, LUA_FILEHANDLE); - if (p == NULL) - lua_pushnil(L); /* not a file */ - else if (isclosed(p)) - lua_pushliteral(L, "closed file"); - else - lua_pushliteral(L, "file"); - return 1; -} -static int f_tostring (lua_State *L) { - LStream *p = tolstream(L); - if (isclosed(p)) - lua_pushliteral(L, "file (closed)"); - else - lua_pushfstring(L, "file (%p)", p->f); - return 1; -} static FILE *tofile (lua_State *L) { - LStream *p = tolstream(L); - if (isclosed(p)) - luaL_error(L, "attempt to use a closed file"); - lua_assert(p->f); - return p->f; +#ifdef LuajitTeX + FILE **f = luaL_checkudata(L,1,LUA_FILEHANDLE); + if (*f == NULL) + luaL_error(L,"attempt to use a closed file"); + return *f; +#else + luaL_Stream *p = ((luaL_Stream *)luaL_checkudata(L, 1, LUA_FILEHANDLE)); + if ((p)->closef == NULL) + luaL_error(L, "attempt to use a closed file"); + lua_assert(p->f); + return p->f; +#endif } - /* -** When creating file handles, always creates a `closed' file handle -** before opening the actual file; so, if there is a memory error, the -** file is not left opened. + HH: A few helpers to avoid reading numbers as strings. For now we put them in their + own namespace. We also have a few helpers that can make io functions tex friendly. */ -static LStream *newprefile (lua_State *L) { - LStream *p = (LStream *)lua_newuserdata(L, sizeof(LStream)); - p->closef = NULL; /* mark file handle as 'closed' */ - luaL_setmetatable(L, LUA_FILEHANDLE); - return p; -} - - -static int aux_close (lua_State *L) { - LStream *p = tolstream(L); - lua_CFunction cf = p->closef; - p->closef = NULL; /* mark stream as closed */ - return (*cf)(L); /* close it */ -} - -static int io_close (lua_State *L) { - if (lua_isnone(L, 1)) /* no argument? */ - lua_getfield(L, LUA_REGISTRYINDEX, IO_OUTPUT); /* use standard output */ - tofile(L); /* make sure argument is an open stream */ - return aux_close(L); +static int readcardinal1(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + if (a == EOF) + lua_pushnil(L); + else + lua_pushinteger(L, a); + return 1; } -static int f_gc (lua_State *L) { - LStream *p = tolstream(L); - if (!isclosed(p) && p->f != NULL) - aux_close(L); /* ignore closed and incompletely open files */ - return 0; +static int readcardinal2(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + if (b == EOF) + lua_pushnil(L); + else + /* (a<<8) | b */ + lua_pushinteger(L, 0x100 * a + b); + return 1; } - -/* -** function to close regular files -*/ -static int io_fclose (lua_State *L) { - LStream *p = tolstream(L); - int res = fclose(p->f); - return luaL_fileresult(L, (res == 0), NULL); +static int readcardinal3(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + int c = getc(f); + if (c == EOF) + lua_pushnil(L); + else + /* (a<<16) | (b<<8) | c */ + lua_pushinteger(L, 0x10000 * a + 0x100 * b + c); + return 1; } - -static LStream *newfile (lua_State *L) { - LStream *p = newprefile(L); - p->f = NULL; - p->closef = &io_fclose; - return p; +static int readcardinal4(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + int c = getc(f); + int d = getc(f); + if (d == EOF) + lua_pushnil(L); + else + /* (a<<24) | (b<<16) | (c<<8) | d */ + lua_pushinteger(L,0x1000000 * a + 0x10000 * b + 0x100 * c + d); + return 1; } - -static void opencheck (lua_State *L, const char *fname, const char *mode) { - LStream *p = newfile(L); - p->f = fopen(fname, mode); - if (p->f == NULL) { - luaL_error(L, "cannot open file " LUA_QS " (%s)", fname, strerror(errno)); - } else { -#ifdef WIN32 - _setmode (fileno (p->f), _O_BINARY); -#endif - if (mode[0]=='r') - recorder_record_input(fname); +static int readinteger1(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + if (a == EOF) + lua_pushnil(L); + else if (a >= 0x80) + lua_pushinteger(L, a - 0x100); else - recorder_record_output(fname); - } + lua_pushinteger(L, a); + return 1; } - -static int io_open (lua_State *L) { - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - LStream *p = newfile(L); - int i = 0; - /* check whether 'mode' matches '[rwa]%+?b?' */ - if (!(mode[i] != '\0' && strchr("rwa", mode[i++]) != NULL && - (mode[i] != '+' || ++i) && /* skip if char is '+' */ - (mode[i] != 'b' || ++i) && /* skip if char is 'b' */ - (mode[i] == '\0'))) - return luaL_error(L, "invalid mode " LUA_QS - " (should match " LUA_QL("[rwa]%%+?b?") ")", mode); - p->f = fopen(filename, mode); - if (p->f == NULL) { - return luaL_fileresult(L, 0, filename) ; - } else { -#ifdef WIN32 - _setmode (fileno (p->f), _O_BINARY); -#endif - if (mode[0]=='r') - recorder_record_input(filename); - else - recorder_record_output(filename); - return 1; - } +static int readinteger2(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + if (b == EOF) + lua_pushnil(L); + else if (a >= 0x80) + lua_pushinteger(L, 0x100 * a + b - 0x10000); + else + lua_pushinteger(L, 0x100 * a + b); + return 1; } -/* -** function to close 'popen' files -*/ -static int io_pclose (lua_State *L) { - LStream *p = tolstream(L); - return luaL_execresult(L, lua_pclose(L, p->f)); +static int readinteger3(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + int c = getc(f); + if (c == EOF) + lua_pushnil(L); + else if (a >= 0x80) + lua_pushinteger(L, 0x10000 * a + 0x100 * b + c - 0x1000000); + else + lua_pushinteger(L, 0x10000 * a + 0x100 * b + c); + return 1; } -static int io_popen(lua_State * L) -{ - int ret = 1; - char *safecmd = NULL; - char *cmdname = NULL; - int allow = 0; - const char *filename = luaL_checkstring(L, 1); - const char *mode = luaL_optstring(L, 2, "r"); - LStream *p = newprefile(L); - - if (shellenabledp <= 0) { +static int readinteger4(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + int c = getc(f); + int d = getc(f); + if (d == EOF) lua_pushnil(L); - lua_pushliteral(L, "All command execution is disabled"); - return 2; - } - /* If restrictedshell == 0, any command is allowed. */ - if (restrictedshell == 0) - allow = 1; + else if (a >= 0x80) + lua_pushinteger(L, 0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000); else - allow = shell_cmd_is_allowed(filename, &safecmd, &cmdname); - - if (allow == 1) { - p->f = lua_popen(L, filename, mode); - p->closef = &io_pclose; - ret = (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; - } else if (allow == 2) { - p->f = lua_popen(L, safecmd, mode); - p->closef = &io_pclose; - ret = (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1; - } else if (allow == 0) { - lua_pushnil(L); - lua_pushliteral(L, "Command execution disabled via shell_escape='p'"); - ret = 2; - } else { - lua_pushnil(L); - lua_pushliteral(L, "Bad command line quoting"); - ret = 2; - } - return ret; + lua_pushinteger(L, 0x1000000 * a + 0x10000 * b + 0x100 * c + d); + return 1; } - -static int io_tmpfile (lua_State *L) { - LStream *p = newfile(L); - p->f = tmpfile(); - return (p->f == NULL) ? luaL_fileresult(L, 0, NULL) : 1; +static int readfixed2(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + if (b == EOF) + lua_pushnil(L); + else if (a >= 0x80) + lua_pushinteger(L, a + b/0xFFFF - 0x100); + else + lua_pushinteger(L, a + b/0xFFFF); + return 1; } - -static FILE *getiofile (lua_State *L, const char *findex) { - LStream *p; - lua_getfield(L, LUA_REGISTRYINDEX, findex); - p = (LStream *)lua_touserdata(L, -1); - if (isclosed(p)) - luaL_error(L, "standard %s file is closed", findex + strlen(IO_PREFIX)); - return p->f; +static int readfixed4(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + int c = getc(f); + int d = getc(f); + if (d == EOF) + lua_pushnil(L); + else if (a >= 0x80) + lua_pushnumber(L, (0x1000000 * a + 0x10000 * b + 0x100 * c + d - 0x100000000)/65536.0); + else + lua_pushnumber(L, (0x1000000 * a + 0x10000 * b + 0x100 * c + d)/65536.0); + /* from ff */ + /* int n = 0x1000000 * a + 0x10000 * b + 0x100 * c + d; */ + /* lua_pushnumber(L,(real) (n>>16) + ((n&0xffff)/65536.0)); */ + return 1; } - -static int g_iofile (lua_State *L, const char *f, const char *mode) { - if (!lua_isnoneornil(L, 1)) { - const char *filename = lua_tostring(L, 1); - if (filename) - opencheck(L, filename, mode); - else { - tofile(L); /* check that it's a valid file handle */ - lua_pushvalue(L, 1); +static int read2dot14(lua_State *L) { + FILE *f = tofile(L); + int a = getc(f); + int b = getc(f); + if (b == EOF) { + lua_pushnil(L); + } else { + int n = 0x100 * a + b; + /* from ff */ + lua_pushnumber(L,(real) ((n<<16)>>(16+14)) + ((n&0x3fff)/16384.0)); } - lua_setfield(L, LUA_REGISTRYINDEX, f); - } - /* return current value */ - lua_getfield(L, LUA_REGISTRYINDEX, f); - return 1; + return 1; } - -static int io_input (lua_State *L) { - return g_iofile(L, IO_INPUT, "r"); +static int getposition(lua_State *L) { + FILE *f = tofile(L); + long p = ftell(f); + if (p<0) + lua_pushnil(L); + else + lua_pushinteger(L, p); + return 1; } - -static int io_output (lua_State *L) { - return g_iofile(L, IO_OUTPUT, "w"); +static int setposition(lua_State *L) { + FILE *f = tofile(L); + long p = lua_tointeger(L,2); + p = fseek(f,p,SEEK_SET); + if (p<0) + lua_pushnil(L); + else + lua_pushinteger(L, p); + return 1; } - -static int io_readline (lua_State *L); - - -static void aux_lines (lua_State *L, int toclose) { - int i; - int n = lua_gettop(L) - 1; /* number of arguments to read */ - /* ensure that arguments will fit here and into 'io_readline' stack */ - luaL_argcheck(L, n <= LUA_MINSTACK - 3, LUA_MINSTACK - 3, "too many options"); - lua_pushvalue(L, 1); /* file handle */ - lua_pushinteger(L, n); /* number of arguments to read */ - lua_pushboolean(L, toclose); /* close/not close file when finished */ - for (i = 1; i <= n; i++) lua_pushvalue(L, i + 1); /* copy arguments */ - lua_pushcclosure(L, io_readline, 3 + n); +static int skipposition(lua_State *L) { + FILE *f = tofile(L); + long p = lua_tointeger(L,2); + p = fseek(f,ftell(f)+p,SEEK_SET); + if (p<0) + lua_pushnil(L); + else + lua_pushinteger(L, p); + return 1; } - -static int f_lines (lua_State *L) { - tofile(L); /* check that it's a valid file handle */ - aux_lines(L, 0); - return 1; +static int readbytetable(lua_State *L) { + FILE *f = tofile(L); + int n = lua_tointeger(L,2); + int i ; + lua_createtable(L, n, 0); + for (i=1;i<=n;i++) { + int a = getc(f); + if (a == EOF) { + break; + } else { + /* + lua_pushinteger(L, i); + lua_pushinteger(L, a); + lua_rawset(L, -3); + */ + lua_pushinteger(L, a); + lua_rawseti(L,-2,i); + } + } + return 1; } - -static int io_lines (lua_State *L) { - int toclose; - if (lua_isnone(L, 1)) lua_pushnil(L); /* at least one argument */ - if (lua_isnil(L, 1)) { /* no file name? */ - lua_getfield(L, LUA_REGISTRYINDEX, IO_INPUT); /* get default input */ - lua_replace(L, 1); /* put it at index 1 */ - tofile(L); /* check that it's a valid file handle */ - toclose = 0; /* do not close it after iteration */ - } - else { /* open a new file */ - const char *filename = luaL_checkstring(L, 1); - opencheck(L, filename, "r"); - lua_replace(L, 1); /* put file at index 1 */ - toclose = 1; /* close it after iteration */ - } - aux_lines(L, toclose); - return 1; +static int readbytes(lua_State *L) { + FILE *f = tofile(L); + int n = lua_tointeger(L,2); + int i = 0; + for (i=1;i<=n;i++) { + int a = getc(f); + if (a == EOF) { + return i-1; + } else { + lua_pushinteger(L, a); + } + } + return n; } -/* -** {====================================================== -** READ -** ======================================================= -*/ - - -static int read_number (lua_State *L, FILE *f) { - lua_Number d; - if (fscanf(f, LUA_NUMBER_SCAN, &d) == 1) { - lua_pushnumber(L, d); /* integer or float */ - return 1; - } - else { - lua_pushnil(L); /* "result" to be removed */ - return 0; /* read fails */ - } +static int recordfilename(lua_State *L) +{ + const char *fname = luaL_checkstring(L, 1); + const char *ftype = lua_tostring(L, 2); + if (fname != NULL && ftype != NULL) { + switch (ftype[1]) { + case 'r': + recorder_record_input(fname); + break; + case 'w': + recorder_record_output(fname); + break; + default: + /* silently ignore */ + break; + } + } else { + /* silently ignore */ + } + return 0; } - -static int test_eof (lua_State *L, FILE *f) { - int c = getc(f); - ungetc(c, f); - lua_pushlstring(L, NULL, 0); - return (c != EOF); +static int checkpermission(lua_State *L) +{ + const char *filename = luaL_checkstring(L, 1); + if (filename == NULL) { + lua_pushboolean(L,0); + lua_pushliteral(L,"no command name given"); + } else if (shellenabledp <= 0) { + lua_pushboolean(L,0); + lua_pushliteral(L,"all command execution is disabled"); + } else if (restrictedshell == 0) { + lua_pushboolean(L,1); + lua_pushliteral(L,"all commands are permitted"); + } else { + char *safecmd = NULL; + char *cmdname = NULL; + switch (shell_cmd_is_allowed(filename, &safecmd, &cmdname)) { + case 0: + lua_pushboolean(L,0); + lua_pushliteral(L, "specific command execution disabled"); + break; + case 1: + lua_pushboolean(L,1); + lua_pushstring(L,filename); + break; + case 2: + lua_pushboolean(L,1); + lua_pushstring(L,safecmd); + break; + default: + lua_pushboolean(L,0); + lua_pushliteral(L, "bad command line quoting"); + break; + } + } + return 2; } -/* this version does not care wether the file has - line endings using an 'alien' convention */ - -static int read_line(lua_State * L, FILE * f, int chop) +static int readline(lua_State *L) { luaL_Buffer buf; int c, d; + FILE *f = tofile(L); luaL_buffinit(L, &buf); while (1) { c = fgetc(f); if (c == EOF) { - luaL_pushresult(&buf); /* close buffer */ - return (lua_rawlen(L, -1) > 0); /* check whether read something */ - }; - if (c == '\n') { - if (!chop) - luaL_addchar(&buf, c); - break; + luaL_pushresult(&buf); + if (lua_rawlen(L, -1) == 0) { + lua_pop(L, 1); + lua_pushnil(L); + } + return 1; + } else if (c == '\n') { + luaL_pushresult(&buf); + return 1; } else if (c == '\r') { - if (!chop) - luaL_addchar(&buf, c); d = fgetc(f); if (d != EOF && d != '\n') { ungetc(d, f); - } else if (!chop) { - luaL_addchar(&buf, d); - } - break; + } + luaL_pushresult(&buf); + return 1; } else { luaL_addchar(&buf, c); } } - luaL_pushresult(&buf); /* close buffer */ - return 1; -} - -#define MAX_SIZE_T (~(size_t)0) - -static void read_all (lua_State *L, FILE *f) { - size_t rlen = LUAL_BUFFERSIZE; /* how much to read in each cycle */ - l_seeknum old, nrlen = 0; /* for testing file size */ - luaL_Buffer b; - luaL_buffinit(L, &b); - /* speed up loading of not too large files: */ - old = l_ftell(f); - if ((l_fseek(f, 0, SEEK_END) >= 0) && - ((nrlen = l_ftell(f)) > 0) && nrlen < 1000 * 1000 * 100) { - rlen = nrlen; - } - l_fseek(f, old, SEEK_SET); - for (;;) { - char *p = luaL_prepbuffsize(&b, rlen); - size_t nr = fread(p, sizeof(char), rlen, f); - luaL_addsize(&b, nr); - if (nr < rlen) break; /* eof? */ - else if (rlen <= (MAX_SIZE_T / 4)) /* avoid buffers too large */ - rlen *= 2; /* double buffer size at each iteration */ - } - luaL_pushresult(&b); /* close buffer */ -} - - -static int read_chars (lua_State *L, FILE *f, size_t n) { - size_t nr; /* number of chars actually read */ - char *p; - luaL_Buffer b; - luaL_buffinit(L, &b); - p = luaL_prepbuffsize(&b, n); /* prepare buffer to read whole block */ - nr = fread(p, sizeof(char), n, f); /* try to read 'n' chars */ - luaL_addsize(&b, nr); - luaL_pushresult(&b); /* close buffer */ - return (nr > 0); /* true iff read something */ -} - - -static int g_read (lua_State *L, FILE *f, int first) { - int nargs = lua_gettop(L) - 1; - int success; - int n; - clearerr(f); - if (nargs == 0) { /* no arguments? */ - success = read_line(L, f, 1); - n = first+1; /* to return 1 result */ - } - else { /* ensure stack space for all results and for auxlib's buffer */ - luaL_checkstack(L, nargs+LUA_MINSTACK, "too many arguments"); - success = 1; - for (n = first; nargs-- && success; n++) { - if (lua_type(L, n) == LUA_TNUMBER) { - size_t l = (size_t)lua_tointeger(L, n); - success = (l == 0) ? test_eof(L, f) : read_chars(L, f, l); - } - else { - const char *p = lua_tostring(L, n); - luaL_argcheck(L, p && p[0] == '*', n, "invalid option"); - switch (p[1]) { - case 'n': /* number */ - success = read_number(L, f); - break; - case 'l': /* line */ - success = read_line(L, f, 1); - break; - case 'L': /* line with end-of-line */ - success = read_line(L, f, 0); - break; - case 'a': /* file */ - read_all(L, f); /* read entire file */ - success = 1; /* always success */ - break; - default: - return luaL_argerror(L, n, "invalid format"); - } - } - } - } - if (ferror(f)) - return luaL_fileresult(L, 0, NULL); - if (!success) { - lua_pop(L, 1); /* remove last result */ - lua_pushnil(L); /* push nil instead */ - } - return n - first; -} - - -static int io_read (lua_State *L) { - return g_read(L, getiofile(L, IO_INPUT), 1); -} - -static int f_read (lua_State *L) { - return g_read(L, tofile(L), 2); -} - - -static int io_readline (lua_State *L) { - LStream *p = (LStream *)lua_touserdata(L, lua_upvalueindex(1)); - int i; - int n = (int)lua_tointeger(L, lua_upvalueindex(2)); - if (isclosed(p)) /* file is already closed? */ - return luaL_error(L, "file is already closed"); - lua_settop(L , 1); - for (i = 1; i <= n; i++) /* push arguments to 'g_read' */ - lua_pushvalue(L, lua_upvalueindex(3 + i)); - n = g_read(L, p->f, 2); /* 'n' is number of results */ - lua_assert(n > 0); /* should return at least a nil */ - if (!lua_isnil(L, -n)) /* read at least one value? */ - return n; /* return them */ - else { /* first result is nil: EOF or error */ - if (n > 1) { /* is there error information? */ - /* 2nd result is error message */ - return luaL_error(L, "%s", lua_tostring(L, -n + 1)); - } - if (lua_toboolean(L, lua_upvalueindex(3))) { /* generator created file? */ - lua_settop(L, 0); - lua_pushvalue(L, lua_upvalueindex(1)); - aux_close(L); /* close it */ - } - return 0; - } } -/* }====================================================== */ - - -static int g_write (lua_State *L, FILE *f, int arg) { - int nargs = lua_gettop(L) - arg; - int status = 1; - for (; nargs--; arg++) { - if (lua_type(L, arg) == LUA_TNUMBER) { - /* optimization: could be done exactly as for strings */ - status = status && - fprintf(f, LUA_NUMBER_FMT, lua_tonumber(L, arg)) > 0; - } - else { - size_t l; - const char *s = luaL_checklstring(L, arg, &l); - status = status && (fwrite(s, sizeof(char), l, f) == l); - } - } - if (status) return 1; /* file handle already on stack top */ - else return luaL_fileresult(L, status, NULL); -} - - -static int io_write (lua_State *L) { - return g_write(L, getiofile(L, IO_OUTPUT), 1); -} - - -static int f_write (lua_State *L) { - FILE *f = tofile(L); - lua_pushvalue(L, 1); /* push file at the stack top (to be returned) */ - return g_write(L, f, 2); -} - - -static int f_seek (lua_State *L) { - static const int mode[] = {SEEK_SET, SEEK_CUR, SEEK_END}; - static const char *const modenames[] = {"set", "cur", "end", NULL}; - FILE *f = tofile(L); - int op = luaL_checkoption(L, 2, "cur", modenames); - lua_Number p3 = luaL_optnumber(L, 3, 0); - l_seeknum offset = (l_seeknum)p3; - luaL_argcheck(L, (lua_Number)offset == p3, 3, - "not an integer in proper range"); - op = l_fseek(f, offset, mode[op]); - if (op) - return luaL_fileresult(L, 0, NULL); /* error */ - else { - lua_pushinteger(L, (lua_Number)l_ftell(f)); - return 1; - } -} - - -static int f_setvbuf (lua_State *L) { - int res; - static const int mode[] = {_IONBF, _IOFBF, _IOLBF}; - static const char *const modenames[] = {"no", "full", "line", NULL}; - FILE *f = tofile(L); - int op = luaL_checkoption(L, 2, NULL, modenames); - lua_Integer sz = luaL_optinteger(L, 3, LUAL_BUFFERSIZE); - if(sz == 0) - res = setvbuf(f, NULL, _IONBF, sz); - else - res = setvbuf(f, NULL, mode[op], sz); - return luaL_fileresult(L, res == 0, NULL); -} - - - -static int io_flush (lua_State *L) { - return luaL_fileresult(L, fflush(getiofile(L, IO_OUTPUT)) == 0, NULL); -} - - -static int f_flush (lua_State *L) { - return luaL_fileresult(L, fflush(tofile(L)) == 0, NULL); -} - - -/* -** functions for 'io' library -*/ -static const luaL_Reg iolib[] = { - {"close", io_close}, - {"flush", io_flush}, - {"input", io_input}, - {"lines", io_lines}, - {"open", io_open}, - {"output", io_output}, - {"popen", io_popen}, - {"read", io_read}, - {"tmpfile", io_tmpfile}, - {"type", io_type}, - {"write", io_write}, - {NULL, NULL} -}; - - -/* -** methods for file handles -*/ -static const luaL_Reg flib[] = { - {"close", io_close}, - {"flush", f_flush}, - {"lines", f_lines}, - {"read", f_read}, - {"seek", f_seek}, - {"setvbuf", f_setvbuf}, - {"write", f_write}, - {"__gc", f_gc}, - {"__tostring", f_tostring}, - {NULL, NULL} +static const luaL_Reg fiolib[] = { + /* helpers */ + { "readcardinal1", readcardinal1 }, + { "readcardinal2", readcardinal2 }, + { "readcardinal3", readcardinal3 }, + { "readcardinal4", readcardinal4 }, + { "readinteger1", readinteger1 }, + { "readinteger2", readinteger2 }, + { "readinteger3", readinteger3 }, + { "readinteger4", readinteger4 }, + { "readfixed2", readfixed2 }, + { "readfixed4", readfixed4 }, + { "read2dot14", read2dot14 }, + { "setposition", setposition }, + { "getposition", getposition }, + { "skipposition", skipposition }, + { "readbytes", readbytes }, + { "readbytetable", readbytetable }, + { "readline", readline }, + /* extras */ + { "recordfilename", recordfilename }, + { "checkpermission", checkpermission }, + /* done */ + {NULL, NULL} }; - -#ifndef LuajitTeX -static void createmeta (lua_State *L) { - luaL_newmetatable(L, LUA_FILEHANDLE); /* create metatable for file handles */ - lua_pushvalue(L, -1); /* push metatable */ - lua_setfield(L, -2, "__index"); /* metatable.__index = metatable */ - luaL_setfuncs(L, flib, 0); /* add file methods to new metatable */ - lua_pop(L, 1); /* pop new metatable */ -} - - -/* -** function to (not) close the standard files stdin, stdout, and stderr -*/ -static int io_noclose (lua_State *L) { - LStream *p = tolstream(L); - p->closef = &io_noclose; /* keep file opened */ - lua_pushnil(L); - lua_pushliteral(L, "cannot close standard file"); - return 2; -} - - -static void createstdfile (lua_State *L, FILE *f, const char *k, - const char *fname) { - LStream *p = newprefile(L); - p->f = f; - p->closef = &io_noclose; - if (k != NULL) { - lua_pushvalue(L, -1); - lua_setfield(L, LUA_REGISTRYINDEX, k); /* add file to registry */ - } - lua_setfield(L, -2, fname); /* add file to module */ -} -#endif - -int open_iolibext (lua_State *L) { -#ifdef LuajitTeX - return luaopen_io(L); -#else - luaL_newlib(L, iolib); /* new module */ - createmeta(L); - /* create (and set) default files */ - createstdfile(L, stdin, IO_INPUT, "stdin"); - createstdfile(L, stdout, IO_OUTPUT, "stdout"); - createstdfile(L, stderr, NULL, "stderr"); - return 1; -#endif +int luaopen_fio(lua_State *L) { + luaL_register(L, "fio", fiolib); + return 1; } diff --git a/Build/source/texk/web2c/luatexdir/lua/lnewtokenlib.c b/Build/source/texk/web2c/luatexdir/lua/lnewtokenlib.c index 373f2d98382..a891c59bcbb 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lnewtokenlib.c +++ b/Build/source/texk/web2c/luatexdir/lua/lnewtokenlib.c @@ -797,6 +797,53 @@ static int run_scan_token(lua_State * L) /* TODO: check for a quick way to set a macro to empty (HH) */ +static int get_meaning(lua_State * L) +{ + const char *name = null; + size_t lname = 0; + int cs, cmd; + if (lua_type(L, 1) == LUA_TSTRING) { + name = lua_tolstring(L, 1, &lname); + cs = string_lookup(name, lname); + cmd = eq_type(cs); + if (cmd >= call_cmd) { + int chr = equiv(cs); + char *str = tokenlist_to_cstring(chr, true, NULL); + lua_pushstring(L, str); + free(str); + return 1; + } + } + return 0; +} + +static int get_macro(lua_State * L) +{ + const char *name = null; + size_t lname = 0; + int cs, cmd; + if (lua_type(L, 1) == LUA_TSTRING) { + name = lua_tolstring(L, 1, &lname); + cs = string_lookup(name, lname); + cmd = eq_type(cs); + if (cmd >= call_cmd) { + /* + Expanding would expand in-place, unless we make a copy which we don't want to + do. So, we just pass the meaning i.e. no: expand_macros_in_tokenlist(chr). + + Actually it would be nice to adapt tokenlist_to_cstring with an extra argument + indicating that we are not interested in the before -> part. + */ + int chr = equiv(cs); + char *str = tokenlist_to_xstring(chr, true, NULL); + lua_pushstring(L, str); + free(str); + return 1; + } + } + return 0; +} + static int set_macro(lua_State * L) { const char *name = null; @@ -950,6 +997,8 @@ static const struct luaL_Reg tokenlib[] = { { "get_protected", lua_tokenlib_get_protected }, /* maybe more setters */ { "set_macro", set_macro }, + { "get_macro", get_macro }, + { "get_meaning", get_meaning }, /* probably never */ /* {"expand", run_expand}, */ /* does not work yet! */ /* {"csname_id", run_get_csname_id}, */ /* yes or no */ diff --git a/Build/source/texk/web2c/luatexdir/lua/loslibext.c b/Build/source/texk/web2c/luatexdir/lua/loslibext.c index 76dd527c0bf..309d636c4f6 100644 --- a/Build/source/texk/web2c/luatexdir/lua/loslibext.c +++ b/Build/source/texk/web2c/luatexdir/lua/loslibext.c @@ -894,11 +894,11 @@ static int os_gettimeofday(lua_State * L) } #endif -static const char repl[] = "0123456789abcdefghijklmnopqrstuvwxyz"; #define MAXTRIES 36*36*36 #ifndef HAVE_MKDTEMP +static const char repl[] = "0123456789abcdefghijklmnopqrstuvwxyz"; static int dirs_made = 0; static char *do_mkdtemp(char *tmpl) @@ -1014,7 +1014,7 @@ static int os_execute(lua_State * L) } -void open_oslibext(lua_State * L, int safer) +void open_oslibext(lua_State * L) { find_env(L); @@ -1037,17 +1037,16 @@ void open_oslibext(lua_State * L, int safer) lua_setfield(L, -2, "gettimeofday"); #endif - if (!safer) { - lua_pushcfunction(L, os_setenv); - lua_setfield(L, -2, "setenv"); - lua_pushcfunction(L, os_exec); - lua_setfield(L, -2, "exec"); - lua_pushcfunction(L, os_spawn); - lua_setfield(L, -2, "spawn"); - lua_pushcfunction(L, os_execute); - lua_setfield(L, -2, "execute"); - lua_pushcfunction(L, os_tmpdir); - lua_setfield(L, -2, "tmpdir"); - } + lua_pushcfunction(L, os_setenv); + lua_setfield(L, -2, "setenv"); + lua_pushcfunction(L, os_exec); + lua_setfield(L, -2, "exec"); + lua_pushcfunction(L, os_spawn); + lua_setfield(L, -2, "spawn"); + lua_pushcfunction(L, os_execute); + lua_setfield(L, -2, "execute"); + lua_pushcfunction(L, os_tmpdir); + lua_setfield(L, -2, "tmpdir"); + lua_pop(L, 1); /* pop the table */ } diff --git a/Build/source/texk/web2c/luatexdir/lua/lstatslib.c b/Build/source/texk/web2c/luatexdir/lua/lstatslib.c index 7473ca214a4..d055dc35cea 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lstatslib.c +++ b/Build/source/texk/web2c/luatexdir/lua/lstatslib.c @@ -121,6 +121,21 @@ static const char *getenginename(void) return engine_name; } +static const char * get_lc_ctype(void) +{ + return lc_ctype; +} + +static const char * get_lc_collate(void) +{ + return lc_collate; +} + +static const char * get_lc_numeric(void) +{ + return lc_numeric; +} + static lua_Number get_luatexhashchars(void) @@ -216,14 +231,36 @@ static int get_hash_size(void) static lua_Number shell_escape_state(void) { if (shellenabledp <= 0) { + /* No shell at all. */ return (lua_Number) 0; } else if (restrictedshell == 0) { + /* Shell has no restriction. */ + return (lua_Number) 2; + } else { + /* Shell has restrictions, see cnf file. */ + return (lua_Number) 1; + } +} + +static lua_Number safer_option_state(void) +{ + if (safer_option == 0) { + return (lua_Number) 0; + } else { + return (lua_Number) 1; + } +} + +static lua_Number kpse_used_state(void) +{ + if (kpse_init == 1) { return (lua_Number) 1; } else { - return (lua_Number) 2; + return (lua_Number) 0; } } + /* temp, for backward compat */ static int init_pool_ptr = 0; @@ -255,14 +292,16 @@ static struct statistic stats[] = { {"log_name", 'S', (void *) &getlogname}, {"banner", 'S', (void *) &getbanner}, {"luatex_version", 'G', &get_luatexversion}, - {"luatex_revision", 'S', (void *) &luatexrevision}, + {"luatex_revision", 'S', (void *) &luatexrevision}, {"luatex_hashtype", 'S', (void *) &get_luatexhashtype}, {"luatex_hashchars", 'N', &get_luatexhashchars}, {"luatex_engine", 'S', (void *) &getenginename}, {"ini_version", 'b', &ini_version}, - {"shell_escape", 'N', &shell_escape_state}, /* be easy on old time usage */ + {"shell_escape", 'N', &shell_escape_state}, + {"safer_option", 'N', &safer_option_state}, + {"kpse_used", 'N', &kpse_used_state}, /* * mem stat */ @@ -310,8 +349,13 @@ static struct statistic stats[] = { {"luabytecode_bytes", 'g', &luabytecode_bytes}, {"luastate_bytes", 'g', &luastate_bytes}, {"callbacks", 'g', &callback_count}, + {"indirect_callbacks", 'g', &saved_callback_count}, + {"lc_ctype", 'S', (void *) &get_lc_ctype}, + {"lc_collate", 'S', (void *) &get_lc_collate}, + {"lc_numeric",'S', (void *) &get_lc_numeric}, + {NULL, 0, 0} }; diff --git a/Build/source/texk/web2c/luatexdir/lua/lstrlibext.c b/Build/source/texk/web2c/luatexdir/lua/lstrlibext.c index 8a474fd09c9..5d5c0d723ac 100644 --- a/Build/source/texk/web2c/luatexdir/lua/lstrlibext.c +++ b/Build/source/texk/web2c/luatexdir/lua/lstrlibext.c @@ -1,5 +1,5 @@ /* lstrlibext.c - + Copyright 2012 Taco Hoekwater This file is part of LuaTeX. @@ -25,100 +25,71 @@ #define LUA_CORE #include "lua.h" +#ifdef LuajitTeX +#include "lauxlib.h" +#include "lualib.h" +#else #include "luaconf.h" #include "lapi.h" #include "lundump.h" +#endif - -static int str_split (lua_State *L) { - size_t l; - size_t i; - int n; - char *q, *p, *orig; - int mult = 0; - const char *s = luaL_checklstring(L, 1, &l); - const char *joiner = luaL_optstring(L, 2, " +"); - lua_newtable(L); - if (l == 0) { - lua_pushvalue(L,1); - lua_rawseti(L,-2,1); - return 1; - } - orig = p = malloc(l+1); - if (p==NULL) { - fprintf(stderr, "fatal: memory exhausted (malloc of %u bytes).\n",(int)(l+1)); - exit(EXIT_FAILURE); - } - strcpy(p,s); - n = 1; - q = p; - - if (*joiner == 0) { - for (i=0;i=0xF0) { - if ((ind+3)<(int)ls && ((unsigned)*(s+ind+1))>=0x80 + if ((ind+3)<(int)ls && ((unsigned)*(s+ind+1))>=0x80 && ((unsigned)*(s+ind+2))>=0x80 && ((unsigned)*(s+ind+3))>=0x80) { numbytes = 4; j = ((unsigned)*(s+ind+1))-128; @@ -211,7 +180,6 @@ static int utfvalues_aux (lua_State *L) { return 0; /* string ended */ } - static int str_utfvalues (lua_State *L) { luaL_checkstring(L, 1); lua_settop(L, 1); @@ -220,8 +188,6 @@ static int str_utfvalues (lua_State *L) { return 1; } - - static int characterpairs_aux (lua_State *L) { size_t ls; char b[2]; @@ -237,7 +203,7 @@ static int characterpairs_aux (lua_State *L) { b[0] = *(s+ind); b[1] = 0; lua_pushlstring(L, b, 1); if (ind+1<(int)ls) { - b[0] = *(s+ind+1); + b[0] = *(s+ind+1); lua_pushlstring(L, b, 1); } else { lua_pushlstring(L, b+1, 0); @@ -247,7 +213,6 @@ static int characterpairs_aux (lua_State *L) { return 0; /* string ended */ } - static int str_characterpairs (lua_State *L) { luaL_checkstring(L, 1); lua_settop(L, 1); @@ -256,65 +221,96 @@ static int str_characterpairs (lua_State *L) { return 1; } -static int bytes_aux (lua_State *L) { +static int characters_aux (lua_State *L) { size_t ls; - unsigned char i; + char b[2]; const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); - int ind = lua_tointeger(L, lua_upvalueindex(2)); + int ind = lua_tointeger(L, lua_upvalueindex(2)); if (ind<(int)ls) { lua_pushinteger(L, (ind+1)); /* iterator */ lua_replace(L, lua_upvalueindex(2)); - i = (unsigned char)*(s+ind); - lua_pushinteger(L, i); /* byte */ + b[0] = *(s+ind); b[1] = 0; + lua_pushlstring(L, b, 1); return 1; } return 0; /* string ended */ } -static int str_bytes (lua_State *L) { +static int str_characters (lua_State *L) { luaL_checkstring(L, 1); lua_settop(L, 1); lua_pushinteger(L, 0); - lua_pushcclosure(L, bytes_aux, 2); + lua_pushcclosure(L, characters_aux, 2); return 1; } -static int bytepairs_aux (lua_State *L) { - size_t ls; - unsigned char i; - const char *s = lua_tolstring(L, lua_upvalueindex(1), &ls); - int ind = lua_tointeger(L, lua_upvalueindex(2)); - if (ind<(int)ls) { - if (ind+1<(int)ls) { - lua_pushinteger(L, (ind+2)); /* iterator */ - } else { - lua_pushinteger(L, (ind+1)); /* iterator */ +static int str_split (lua_State *L) { + size_t l; + size_t i; + int n; + char *q, *p, *orig; + int mult = 0; + const char *s = luaL_checklstring(L, 1, &l); + const char *joiner = luaL_optstring(L, 2, " +"); + lua_newtable(L); + if (l == 0) { + lua_pushvalue(L,1); + lua_rawseti(L,-2,1); + return 1; + } + orig = p = malloc(l+1); + if (p==NULL) { + fprintf(stderr, "fatal: memory exhausted (malloc of %u bytes).\n",(int)(l+1)); + exit(EXIT_FAILURE); + } + strcpy(p,s); + n = 1; + q = p; + + if (*joiner == 0) { + for (i=0;i -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c - -#include "ptexlib.h" - -#include - -#include "lua/luatex-api.h" - -/* internalized strings: see luatex-api.h */ -set_make_keys; - -@ -This file is getting a bit messy, but it is not simple to fix unilaterally. - -Better to wait until Karl has some time (after texlive 2008) so we can -synchronize with kpathsea. One problem, for instance, is that I would -like to resolve the full executable path. |kpse_set_program_name()| does -that, indirectly (by setting SELFAUTOLOC in the environment), but it -does much more, making it hard to use for our purpose. - -In fact, it sets three C variables: - - |kpse_invocation_name| |kpse_invocation_short_name| |kpse->program_name| - -and five environment variables: - - SELFAUTOLOC SELFAUTODIR SELFAUTOPARENT SELFAUTOGRANDPARENT progname - -@c -const_string LUATEX_IHELP[] = { - "Usage: " my_name " --lua=FILE [OPTION]... [TEXNAME[.tex]] [COMMANDS]", - " or: " my_name " --lua=FILE [OPTION]... \\FIRST-LINE", - " or: " my_name " --lua=FILE [OPTION]... &FMT ARGS", - " Run " MyName " on TEXNAME, usually creating TEXNAME.pdf.", - " Any remaining COMMANDS are processed as luatex input, after TEXNAME is read.", - "", - " Alternatively, if the first non-option argument begins with a backslash,", - " " my_name " interprets all non-option arguments as an input line.", - "", - " Alternatively, if the first non-option argument begins with a &, the", - " next word is taken as the FMT to read, overriding all else. Any", - " remaining arguments are processed as above.", - "", - " If no arguments or options are specified, prompt for input.", - "", - " The following regular options are understood: ", - "", - " --credits display credits and exit", - " --debug-format enable format debugging", - " --draftmode switch on draft mode (generates no output PDF)", - " --[no-]file-line-error disable/enable file:line:error style messages", - " --[no-]file-line-error-style aliases of --[no-]file-line-error", - " --fmt=FORMAT load the format file FORMAT", - " --halt-on-error stop processing at the first error", - " --help display help and exit", - " --ini be ini" my_name ", for dumping formats", - " --interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/scrollmode/errorstopmode)", - " --jobname=STRING set the job name to STRING", - " --kpathsea-debug=NUMBER set path searching debugging flags according to the bits of NUMBER", - " --lua=FILE load and execute a lua initialization script", - " --[no-]mktex=FMT disable/enable mktexFMT generation (FMT=tex/tfm)", - " --nosocket disable the lua socket library", - " --output-comment=STRING use STRING for DVI file comment instead of date (no effect for PDF)", - " --output-directory=DIR use existing DIR as the directory to write files in", - " --output-format=FORMAT use FORMAT for job output; FORMAT is 'dvi' or 'pdf'", - " --progname=STRING set the program name to STRING", - " --recorder enable filename recorder", - " --safer disable easily exploitable lua commands", - " --[no-]shell-escape disable/enable system commands", - " --shell-restricted restrict system commands to a list of commands given in texmf.cnf", - " --synctex=NUMBER enable synctex", - " --utc init time to UTC", - " --version display version and exit", - "", - "Alternate behaviour models can be obtained by special switches", - "", - " --luaonly run a lua file, then exit", - " --luaconly byte-compile a lua file, then exit", - " --luahashchars the bits used by current Lua interpreter for strings hashing", -#ifdef LuajitTeX - " --jiton turns the JIT compiler on (default off)", - " --jithash=STRING choose the hash function for the lua strings (lua51|luajit20: default lua51)", -#endif - "", - "See the reference manual for more information about the startup process.", - NULL -}; - -/* - " --8bit ignored, input is assumed to be in UTF-8 encoding", - " --default-translate-file=FILE ignored, input is assumed to be in UTF-8 encoding", - " --etex ignored, the etex extensions are always active", - " --disable-write18 disable \\write18{SHELL COMMAND}", - " --enable-write18 enable \\write18{SHELL COMMAND}", - " --[no-]parse-first-line ignored", - " --translate-file=FILE ignored, input is assumed to be in UTF-8 encoding", -*/ - -@ The return value will be the directory of the executable, e.g.: \.{c:/TeX/bin} -@c -static char *ex_selfdir(char *argv0) -{ -#if defined(WIN32) -#if defined(__MINGW32__) - char path[PATH_MAX], *fp; - - /* SearchPath() always gives back an absolute directory */ - if (SearchPath(NULL, argv0, ".exe", PATH_MAX, path, NULL) == 0) - FATAL1("Can't determine where the executable %s is.\n", argv0); - /* slashify the dirname */ - for (fp = path; fp && *fp; fp++) - if (IS_DIR_SEP(*fp)) - *fp = DIR_SEP; -#else /* __MINGW32__ */ -#define PATH_MAX 512 - char short_path[PATH_MAX], path[PATH_MAX], *fp; - - /* SearchPath() always gives back an absolute directory */ - if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0) - FATAL1("Can't determine where the executable %s is.\n", argv0); - if (getlongpath(path, short_path, sizeof(path)) == 0) { - FATAL1("This path points to an invalid file : %s\n", short_path); - } -#endif /* __MINGW32__ */ - return xdirname(path); -#else /* WIN32 */ - return kpse_selfdir(argv0); -#endif -} - -@ @c -static void prepare_cmdline(lua_State * L, char **av, int ac, int zero_offset) -{ - int i; - char *s; - luaL_checkstack(L, ac + 3, "too many arguments to script"); - lua_createtable(L, 0, 0); - for (i = 0; i < ac; i++) { - lua_pushstring(L, av[i]); - lua_rawseti(L, -2, (i - zero_offset)); - } - lua_setglobal(L, "arg"); - lua_getglobal(L, "os"); - s = ex_selfdir(argv[0]); - lua_pushstring(L, s); - xfree(s); - lua_setfield(L, -2, "selfdir"); - return; -} - - -@ @c -int kpse_init = -1; - -@ @c -string input_name = NULL; - -static string user_progname = NULL; - -char *startup_filename = NULL; -int lua_only = 0; -int lua_offset = 0; -unsigned char show_luahashchars = 0; - -#ifdef LuajitTeX -int luajiton = 0; -char *jithash_hashname = NULL; -#endif - -int safer_option = 0; -int nosocket_option = 0; -int utc_option = 0; - -@ Reading the options. - -@ Test whether getopt found an option ``A''. -Assumes the option index is in the variable |option_index|, and the -option table in a variable |long_options|. - -@c -#define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a) - -/* - SunOS cc can't initialize automatic structs, so make this static. -*/ - -/* - Nota Bene: we still intercept some options that other engines handle - so that existing scripted usage will not fail. -*/ - -static struct option long_options[] = { - {"fmt", 1, 0, 0}, - {"lua", 1, 0, 0}, - {"luaonly", 0, 0, 0}, - {"luahashchars", 0, 0, 0}, -#ifdef LuajitTeX - {"jiton", 0, 0, 0}, - {"jithash", 1, 0, 0}, -#endif - {"safer", 0, &safer_option, 1}, - {"utc", 0, &utc_option, 1}, - {"nosocket", 0, &nosocket_option, 1}, - {"help", 0, 0, 0}, - {"ini", 0, &ini_version, 1}, - {"interaction", 1, 0, 0}, - {"halt-on-error", 0, &haltonerrorp, 1}, - {"kpathsea-debug", 1, 0, 0}, - {"progname", 1, 0, 0}, - {"version", 0, 0, 0}, - {"credits", 0, 0, 0}, - {"recorder", 0, 0, 0}, - {"etex", 0, 0, 0}, - {"output-comment", 1, 0, 0}, - {"output-directory", 1, 0, 0}, - {"draftmode", 0, 0, 0}, - {"output-format", 1, 0, 0}, - {"shell-escape", 0, &shellenabledp, 1}, - {"no-shell-escape", 0, &shellenabledp, -1}, - {"enable-write18", 0, &shellenabledp, 1}, - {"disable-write18", 0, &shellenabledp, -1}, - {"shell-restricted", 0, 0, 0}, - {"debug-format", 0, &debug_format_file, 1}, - {"file-line-error-style", 0, &filelineerrorstylep, 1}, - {"no-file-line-error-style", 0, &filelineerrorstylep, -1}, - - /* Shorter option names for the above. */ - - {"file-line-error", 0, &filelineerrorstylep, 1}, - {"no-file-line-error", 0, &filelineerrorstylep, -1}, - {"jobname", 1, 0, 0}, - {"parse-first-line", 0, &parsefirstlinep, 1}, - {"no-parse-first-line", 0, &parsefirstlinep, -1}, - {"translate-file", 1, 0, 0}, - {"default-translate-file", 1, 0, 0}, - {"8bit", 0, 0, 0}, - {"mktex", 1, 0, 0}, - {"no-mktex", 1, 0, 0}, - - /* Synchronization: just like "interaction" above */ - - {"synctex", 1, 0, 0}, - {0, 0, 0, 0} -}; - -@ @c -int lua_numeric_field_by_index(lua_State * L, int name_index, int dflt) -{ - register int i = dflt; - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - if (lua_type(L, -1) == LUA_TNUMBER) { - i = lua_roundnumber(L, -1); - } - lua_pop(L, 1); - return i; -} - -@ @c -unsigned int lua_unsigned_numeric_field_by_index(lua_State * L, int name_index, int dflt) -{ - register unsigned int i = dflt; - lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ - lua_rawget(L, -2); - if (lua_type(L, -1) == LUA_TNUMBER) { - i = lua_uroundnumber(L, -1); - } - lua_pop(L, 1); - return i; -} - -@ @c -static int recorderoption = 0; - -static void parse_options(int ac, char **av) -{ -#ifdef WIN32 -/* save argc and argv */ - int sargc = argc; - char **sargv = argv; -#endif - int g; /* `getopt' return code. */ - int option_index; - char *firstfile = NULL; - opterr = 0; /* dont whine */ -#ifdef LuajitTeX - if ((strstr(argv[0], "luajittexlua") != NULL) || - (strstr(argv[0], "texluajit") != NULL)) { -#else - if ((strstr(argv[0], "luatexlua") != NULL) || - (strstr(argv[0], "texlua") != NULL)) { -#endif - lua_only = 1; - luainit = 1; - } - for (;;) { - g = getopt_long_only(ac, av, "+", long_options, &option_index); - - if (g == -1) /* End of arguments, exit the loop. */ - break; - if (g == '?') { /* Unknown option. */ - if (!luainit) - fprintf(stderr,"%s: unrecognized option '%s'\n", argv[0], argv[optind-1]); - continue; - } - - assert(g == 0); /* We have no short option names. */ - - if (ARGUMENT_IS("luaonly")) { - lua_only = 1; - lua_offset = optind; - luainit = 1; - } else if (ARGUMENT_IS("lua")) { - startup_filename = optarg; - lua_offset = (optind - 1); - luainit = 1; -#ifdef LuajitTeX - } else if (ARGUMENT_IS("jiton")) { - luajiton = 1; - } else if (ARGUMENT_IS("jithash")) { - size_t len = strlen(optarg); - if (len<16) { - jithash_hashname = optarg; - } else { - WARNING2("hash name truncated to 15 characters from %d. (%s)", (int) len, optarg); - jithash_hashname = (string) xmalloc(16); - strncpy(jithash_hashname, optarg, 15); - jithash_hashname[15] = 0; - } -#endif - } else if (ARGUMENT_IS("luahashchars")) { - show_luahashchars = 1; - } else if (ARGUMENT_IS("kpathsea-debug")) { - kpathsea_debug |= atoi(optarg); - } else if (ARGUMENT_IS("progname")) { - user_progname = optarg; - } else if (ARGUMENT_IS("jobname")) { - c_job_name = optarg; - } else if (ARGUMENT_IS("fmt")) { - dump_name = optarg; - } else if (ARGUMENT_IS("output-directory")) { - output_directory = optarg; - } else if (ARGUMENT_IS("output-comment")) { - size_t len = strlen(optarg); - if (len < 256) { - output_comment = optarg; - } else { - WARNING2("Comment truncated to 255 characters from %d. (%s)", (int) len, optarg); - output_comment = (string) xmalloc(256); - strncpy(output_comment, optarg, 255); - output_comment[255] = 0; - } - } else if (ARGUMENT_IS("shell-restricted")) { - shellenabledp = 1; - restrictedshell = 1; - } else if (ARGUMENT_IS("output-format")) { - output_mode_option = 1; - if (strcmp(optarg, "dvi") == 0) { - output_mode_value = 0; - } else if (strcmp(optarg, "pdf") == 0) { - output_mode_value = 1; - } else { - WARNING1("Ignoring unknown value `%s' for --output-format",optarg); - output_mode_option = 0; - } - } else if (ARGUMENT_IS("draftmode")) { - draft_mode_option = 1; - draft_mode_value = 1; - } else if (ARGUMENT_IS("mktex")) { - kpse_maketex_option(optarg, true); - } else if (ARGUMENT_IS("no-mktex")) { - kpse_maketex_option(optarg, false); - } else if (ARGUMENT_IS("interaction")) { - /* These numbers match CPP defines */ - if (STREQ(optarg, "batchmode")) { - interactionoption = 0; - } else if (STREQ(optarg, "nonstopmode")) { - interactionoption = 1; - } else if (STREQ(optarg, "scrollmode")) { - interactionoption = 2; - } else if (STREQ(optarg, "errorstopmode")) { - interactionoption = 3; - } else { - WARNING1("Ignoring unknown argument `%s' to --interaction", optarg); - } - } else if (ARGUMENT_IS("synctex")) { - /* Synchronize TeXnology: catching the command line option as a long */ - synctexoption = (int) strtol(optarg, NULL, 0); - } else if (ARGUMENT_IS("recorder")) { - recorderoption = 1 ; - } else if (ARGUMENT_IS("help")) { - usagehelp(LUATEX_IHELP, BUG_ADDRESS); - } else if (ARGUMENT_IS("version")) { - print_version_banner(); - /* *INDENT-OFF* */ - puts("\n\nExecute '" my_name " --credits' for credits and version details.\n\n" - "There is NO warranty. Redistribution of this software is covered by\n" - "the terms of the GNU General Public License, version 2 or (at your option)\n" - "any later version. For more information about these matters, see the file\n" - "named COPYING and the LuaTeX source.\n\n" - "LuaTeX is Copyright 2017 Taco Hoekwater and the LuaTeX Team.\n"); - /* *INDENT-ON* */ - uexit(0); - } else if (ARGUMENT_IS("credits")) { - char *versions; - initversionstring(&versions); - print_version_banner(); - /* *INDENT-OFF* */ - puts("\n\nThe LuaTeX team is Hans Hagen, Hartmut Henkel, Taco Hoekwater, Luigi Scarso.\n\n" - MyName " merges and builds upon (parts of) the code from these projects:\n\n" - "tex : Donald Knuth\n" - "etex : Peter Breitenlohner, Phil Taylor and friends\n" - "omega : John Plaice and Yannis Haralambous\n" - "aleph : Giuseppe Bilotta\n" - "pdftex : Han The Thanh and friends\n" - "kpathsea : Karl Berry, Olaf Weber and others\n" - "lua : Roberto Ierusalimschy, Waldemar Celes and Luiz Henrique de Figueiredo\n" - "metapost : John Hobby, Taco Hoekwater and friends\n" - "poppler : Derek Noonburg, Kristian H\\ogsberg (partial)\n" - "fontforge : George Williams (partial)\n" - "luajit : Mike Pall (used in LuajitTeX)\n"); - /* *INDENT-ON* */ - puts(versions); - uexit(0); - } - } - /* attempt to find |input_name| / |dump_name| */ - if (lua_only) { - if (argv[optind]) { - startup_filename = xstrdup(argv[optind]); - lua_offset = optind; - } - } else if (argv[optind] && argv[optind][0] == '&') { - dump_name = xstrdup(argv[optind] + 1); - } else if (argv[optind] && argv[optind][0] != '\\') { - if (argv[optind][0] == '*') { - input_name = xstrdup(argv[optind] + 1); - } else { - firstfile = xstrdup(argv[optind]); - if ((strstr(firstfile, ".lua") == - firstfile + strlen(firstfile) - 4) - || (strstr(firstfile, ".luc") == - firstfile + strlen(firstfile) - 4) - || (strstr(firstfile, ".LUA") == - firstfile + strlen(firstfile) - 4) - || (strstr(firstfile, ".LUC") == - firstfile + strlen(firstfile) - 4)) { - if (startup_filename == NULL) { - startup_filename = firstfile; - lua_offset = optind; - lua_only = 1; - luainit = 1; - } - } else { - input_name = firstfile; - } - } -#ifdef WIN32 - } else if (sargc > 1 && sargv[sargc-1] && sargv[sargc-1][0] != '-' && - sargv[sargc-1][0] != '\\') { - if (sargv[sargc-1][0] == '&') - dump_name = xstrdup(sargv[sargc-1] + 1); - else { - if (sargv[sargc-1][0] == '*') - input_name = xstrdup(sargv[sargc-1] + 1); - else - input_name = xstrdup(sargv[sargc-1]); - sargv[sargc-1] = normalize_quotes(input_name, "argument"); - } - if (safer_option) /* --safer implies --nosocket */ - nosocket_option = 1; - return; -#endif - } - if (safer_option) /* --safer implies --nosocket */ - nosocket_option = 1; - /* Finalize the input filename. */ - if (input_name != NULL) { - argv[optind] = normalize_quotes(input_name, "argument"); - } -} - -@ test for readability -@c -#define is_readable(a) (stat(a,&finfo)==0) && S_ISREG(finfo.st_mode) && \ - (f=fopen(a,"r")) != NULL && !fclose(f) - -@ @c -static char *find_filename(char *name, const char *envkey) -{ - struct stat finfo; - char *dirname = NULL; - char *filename = NULL; - FILE *f; - if (is_readable(name)) { - return name; - } else { - dirname = getenv(envkey); - if ((dirname != NULL) && strlen(dirname)) { - dirname = xstrdup(getenv(envkey)); - if (*(dirname + strlen(dirname) - 1) == '/') { - *(dirname + strlen(dirname) - 1) = 0; - } - filename = xmalloc((unsigned) (strlen(dirname) + strlen(name) + 2)); - filename = concat3(dirname, "/", name); - xfree(dirname); - if (is_readable(filename)) { - return filename; - } - xfree(filename); - } - } - return NULL; -} - -@ @c - -static void init_kpse(void) -{ - if (!user_progname) { - user_progname = dump_name; - } else if (!dump_name) { - dump_name = user_progname; - } - if (!user_progname) { - if (ini_version) { - if (input_name) { - char *p = input_name + strlen(input_name) - 1; - while (p >= input_name) { - if (IS_DIR_SEP (*p)) { - p++; - input_name = p; - break; - } - p--; - } - user_progname = remove_suffix (input_name); - } - if (!user_progname) { - user_progname = kpse_program_basename(argv[0]); - } - } else { - if (!dump_name) { - dump_name = kpse_program_basename(argv[0]); - } - user_progname = dump_name; - } - } - kpse_set_program_enabled(kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT, - kpse_src_compile); - - kpse_set_program_name(argv[0], user_progname); - init_shell_escape(); /* set up 'restrictedshell' */ - init_start_time(); - program_name_set = 1 ; - if (recorderoption) { - recorder_enabled = 1; - } -} - -@ @c -static void fix_dumpname(void) -{ - int dist; - if (dump_name) { - /* adjust array for Pascal and provide extension, if needed */ - dist = (int) (strlen(dump_name) - strlen(DUMP_EXT)); - if (strstr(dump_name, DUMP_EXT) == dump_name + dist) - TEX_format_default = dump_name; - else - TEX_format_default = concat(dump_name, DUMP_EXT); - } else { - /* For |dump_name| to be NULL is a bug. */ - if (!ini_version) { - fprintf(stdout, "no format given, quitting\n"); - exit(1); - } - } -} - -@ lua require patch - -@ Auxiliary function for kpse search - -@c -static const char *luatex_kpse_find_aux(lua_State *L, const char *name, - kpse_file_format_type format, const char *errname) -{ - const char *filename; - const char *altname; - altname = luaL_gsub(L, name, ".", "/"); /* Lua convention */ - filename = kpse_find_file(altname, format, false); - if (filename == NULL) { - filename = kpse_find_file(name, format, false); - } - if (filename == NULL) { - lua_pushfstring(L, "\n\t[kpse %s searcher] file not found: " LUA_QS, errname, name); - } - return filename; -} - -@ The lua search function. - -When kpathsea is not initialized, then it runs the -normal lua function that is saved in the registry, otherwise -it uses kpathsea. - -two registry ref variables are needed: one for the actual lua -function, the other for its environment . - -@c -static int lua_loader_function = 0; - -static int luatex_kpse_lua_find(lua_State * L) -{ - const char *filename; - const char *name; - name = luaL_checkstring(L, 1); - if (program_name_set == 0) { - lua_rawgeti(L, LUA_REGISTRYINDEX, lua_loader_function); - lua_pushvalue(L, -2); - lua_call(L, 1, 1); - return 1; - } - filename = luatex_kpse_find_aux(L, name, kpse_lua_format, "lua"); - if (filename == NULL) - return 1; /* library not found in this path */ - if (luaL_loadfile(L, filename) != 0) { - luaL_error(L, "error loading module %s from file %s:\n\t%s", - lua_tostring(L, 1), filename, lua_tostring(L, -1)); - } - return 1; /* library loaded successfully */ -} - -@ @c -static int clua_loader_function = 0; -extern int searcher_C_luatex (lua_State *L, const char *name, const char *filename); - -static int luatex_kpse_clua_find(lua_State * L) -{ - const char *filename; - const char *name; - if (safer_option) { - lua_pushliteral(L, "\n\t[C searcher disabled in safer mode]"); - return 1; /* library not found in this path */ - } - name = luaL_checkstring(L, 1); - if (program_name_set == 0) { - lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function); - lua_pushvalue(L, -2); - lua_call(L, 1, 1); - return 1; - } else { - const char *path_saved; - char *prefix, *postfix, *p, *total; - char *extensionless; - char *temp_name; - int j; - filename = luatex_kpse_find_aux(L, name, kpse_clua_format, "C"); - if (filename == NULL) - return 1; /* library not found in this path */ - extensionless = strdup(filename); - if (!extensionless) - return 1; /* allocation failure */ - /* Fix Issue 850: replace '.' with LUA_DIRSEP */ - temp_name = strdup(name); - for(j=0; ; j++){ - if ((unsigned char)temp_name[j]=='\0') { - break; - } - if ((unsigned char)temp_name[j]=='.'){ - temp_name[j]=LUA_DIRSEP[0]; - } - } - p = strstr(extensionless, temp_name); - if (!p) return 1; /* this would be exceedingly weird */ - *p = '\0'; - prefix = strdup(extensionless); - if (!prefix) return 1; /* allocation failure */ - postfix = strdup(p+strlen(name)); - if (!postfix) return 1; /* allocation failure */ - total = malloc(strlen(prefix)+strlen(postfix)+2); - if (!total) return 1; /* allocation failure */ - snprintf(total,strlen(prefix)+strlen(postfix)+2, "%s?%s", prefix, postfix); - /* save package.path */ - lua_getglobal(L,"package"); - lua_getfield(L,-1,"cpath"); - path_saved = lua_tostring(L,-1); - lua_pop(L,1); - /* set package.path = "?" */ - lua_pushstring(L,total); - lua_setfield(L,-2,"cpath"); - lua_pop(L,1); /* pop "package" */ - /* run function */ - lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function); - lua_pushstring(L, name); - lua_call(L, 1, 1); - /* restore package.path */ - lua_getglobal(L,"package"); - lua_pushstring(L,path_saved); - lua_setfield(L,-2,"cpath"); - lua_pop(L,1); /* pop "package" */ - free(extensionless); - free(total); - free(temp_name); - return 1; - } -} - -@ Setting up the new search functions. - -This replaces package.searchers[2] and package.searchers[3] with the -functions defined above. - -@c -static void setup_lua_path(lua_State * L) -{ - lua_getglobal(L, "package"); -#ifdef LuajitTeX - lua_getfield(L, -1, "loaders"); -#else - lua_getfield(L, -1, "searchers"); -#endif - lua_rawgeti(L, -1, 2); /* package.searchers[2] */ - lua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushcfunction(L, luatex_kpse_lua_find); - lua_rawseti(L, -2, 2); /* replace the normal lua loader */ - - lua_rawgeti(L, -1, 3); /* package.searchers[3] */ - clua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushcfunction(L, luatex_kpse_clua_find); - lua_rawseti(L, -2, 3); /* replace the normal lua lib loader */ - - lua_pop(L, 2); /* pop the array and table */ -} - -@ helper variables for the safe keeping of table ids - -@c -int tex_table_id; -int pdf_table_id; -int token_table_id; -int node_table_id; - -@ @c -int l_pack_type_index [PACK_TYPE_SIZE] ; -int l_group_code_index [GROUP_CODE_SIZE]; -int l_local_par_index [LOCAL_PAR_SIZE]; -int l_math_style_name_index [MATH_STYLE_NAME_SIZE]; -int l_dir_par_index [DIR_PAR_SIZE]; -int l_dir_text_index [DIR_TEXT_SIZE]; - -int img_parms [img_parms_max]; -int img_pageboxes [img_pageboxes_max]; - -int lua_show_valid_list(lua_State *L, const char **list, int max) -{ - int i; - lua_newtable(L); - for (i = 0; i < max; i++) { - lua_pushinteger(L,i+1); - lua_pushstring(L, list[i]); - lua_settable(L, -3); - } - return 1; -} - -int lua_show_valid_keys(lua_State *L, int *list, int max) -{ - int i; - lua_newtable(L); - for (i = 0; i < max; i++) { - lua_pushinteger(L,i+1); - lua_rawgeti(L, LUA_REGISTRYINDEX, list[i]); - lua_settable(L, -3); - } - return 1; -} - -#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) -char **suffixlist; - -# define EXE_SUFFIXES ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.ws;.tcl;.py;.pyw" - -@ @c -static void mk_suffixlist(void) -{ - char **p; - char *q, *r, *v; - int n; - -# if defined(__CYGWIN__) - v = xstrdup(EXE_SUFFIXES); -# else - v = (char *) getenv("PATHEXT"); - if (v) /* strlwr() exists also in MingW */ - v = (char *) strlwr(xstrdup(v)); - else - v = xstrdup(EXE_SUFFIXES); -# endif - - q = v; - n = 0; - - while ((r = strchr(q, ';')) != NULL) { - n++; - r++; - q = r; - } - if (*q) - n++; - suffixlist = (char **) xmalloc((n + 2) * sizeof(char *)); - p = suffixlist; - *p = xstrdup(".dll"); - p++; - q = v; - while ((r = strchr(q, ';')) != NULL) { - *r = '\0'; - *p = xstrdup(q); - p++; - r++; - q = r; - } - if (*q) { - *p = xstrdup(q); - p++; - } - *p = NULL; - free(v); -} -#endif - -@ @c -void lua_initialize(int ac, char **av) -{ - char *given_file = NULL; - char *banner; - /*int kpse_init;*/ - size_t len; - int starttime; - int utc; - static char LC_CTYPE_C[] = "LC_CTYPE=C"; - static char LC_COLLATE_C[] = "LC_COLLATE=C"; - static char LC_NUMERIC_C[] = "LC_NUMERIC=C"; - static char engine_luatex[] = "engine=" my_name; - /* Save to pass along to topenin. */ - const char *fmt = "This is " MyName ", Version %s" WEB2CVERSION; - argc = ac; - argv = av; - len = strlen(fmt) + strlen(luatex_version_string) ; - banner = xmalloc(len); - sprintf(banner, fmt, luatex_version_string); - luatex_banner = banner; - kpse_invocation_name = kpse_program_basename(argv[0]); - - /* be 'luac' */ - if (argc >1) { -#ifdef LuajitTeX - if (FILESTRCASEEQ(kpse_invocation_name, "texluajitc")) - exit(luac_main(ac, av)); - if (STREQ(argv[1], "--luaconly") || STREQ(argv[1], "--luac")) { - char *argv1 = xmalloc (strlen ("luajittex") + 1); - av[1] = argv1; - strcpy (av[1], "luajittex"); - exit(luac_main(--ac, ++av)); - } -#else - if (FILESTRCASEEQ(kpse_invocation_name, "texluac")) - exit(luac_main(ac, av)); - if (STREQ(argv[1], "--luaconly") || STREQ(argv[1], "--luac")) { - strcpy(av[1], "luatex"); - exit(luac_main(--ac, ++av)); - } -#endif - } -#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) - mk_suffixlist(); -#endif - - /* Must be initialized before options are parsed. */ - interactionoption = 4; - dump_name = NULL; - - /* 0 means "disable Synchronize TeXnology". - synctexoption is a *.web variable. - We initialize it to a weird value to catch the -synctex command line flag - At runtime, if synctexoption is not |INT_MAX|, then it contains the command line option provided, - otherwise no such option was given by the user. */ -#define SYNCTEX_NO_OPTION INT_MAX - synctexoption = SYNCTEX_NO_OPTION; - - /* parse commandline */ - parse_options(ac, av); - if (lua_only) - shellenabledp = true; - - /* make sure that the locale is 'sane' (for lua) */ - - putenv(LC_CTYPE_C); - putenv(LC_COLLATE_C); - putenv(LC_NUMERIC_C); - - /* this is sometimes needed */ - putenv(engine_luatex); - - luainterpreter(); - - /* init internalized strings */ - set_init_keys; - - lua_pushstring(Luas,"lua.functions"); - lua_newtable(Luas); - lua_settable(Luas,LUA_REGISTRYINDEX); - - /* here start the key definitions */ - set_l_pack_type_index; - set_l_group_code_index; - set_l_local_par_index; - set_l_math_style_name_index; - set_l_dir_par_index; - set_l_dir_text_index; - - set_l_img_keys_index; - set_l_img_pageboxes_index; - - prepare_cmdline(Luas, argv, argc, lua_offset); /* collect arguments */ - setup_lua_path(Luas); - - if (startup_filename != NULL) { - given_file = xstrdup(startup_filename); - if (lua_only) { - xfree(startup_filename); - } - startup_filename = find_filename(given_file, "LUATEXDIR"); - } - /* now run the file */ - if (startup_filename != NULL) { - char *v1; - /* hide the 'tex' and 'pdf' table */ - tex_table_id = hide_lua_table(Luas, "tex"); - token_table_id = hide_lua_table(Luas, "token"); - node_table_id = hide_lua_table(Luas, "node"); - pdf_table_id = hide_lua_table(Luas, "pdf"); - - if (luaL_loadfile(Luas, startup_filename)) { - fprintf(stdout, "%s\n", lua_tostring(Luas, -1)); - exit(1); - } - /* */ - init_tex_table(Luas); - if (lua_pcall(Luas, 0, 0, 0)) { - fprintf(stdout, "%s\n", lua_tostring(Luas, -1)); - lua_traceback(Luas); - exit(1); - } - /* no filename? quit now! */ - if (!input_name) { - get_lua_string("texconfig", "jobname", &input_name); - } - if (!dump_name) { - get_lua_string("texconfig", "formatname", &dump_name); - } - if (lua_only) { - if (given_file) - free(given_file); - /* this is not strictly needed but it pleases valgrind */ - lua_close(Luas); - exit(0); - } - /* unhide the 'tex' and 'pdf' table */ - unhide_lua_table(Luas, "tex", tex_table_id); - unhide_lua_table(Luas, "pdf", pdf_table_id); - unhide_lua_table(Luas, "token", token_table_id); - unhide_lua_table(Luas, "node", node_table_id); - - /* |kpse_init| */ - kpse_init = -1; - get_lua_boolean("texconfig", "kpse_init", &kpse_init); - - if (kpse_init != 0) { - luainit = 0; /* re-enable loading of texmf.cnf values, see luatex.ch */ - init_kpse(); - } - /* |prohibit_file_trace| (boolean) */ - tracefilenames = 1; - get_lua_boolean("texconfig", "trace_file_names", &tracefilenames); - - /* |file_line_error| */ - filelineerrorstylep = false; - get_lua_boolean("texconfig", "file_line_error", &filelineerrorstylep); - - /* |halt_on_error| */ - haltonerrorp = false; - get_lua_boolean("texconfig", "halt_on_error", &haltonerrorp); - - /* |restrictedshell| */ - v1 = NULL; - get_lua_string("texconfig", "shell_escape", &v1); - if (v1) { - if (*v1 == 't' || *v1 == 'y' || *v1 == '1') { - shellenabledp = 1; - } else if (*v1 == 'p') { - shellenabledp = 1; - restrictedshell = 1; - } - free(v1); - } - /* If shell escapes are restricted, get allowed cmds from cnf. */ - if (shellenabledp && restrictedshell == 1) { - v1 = NULL; - get_lua_string("texconfig", "shell_escape_commands", &v1); - if (v1) { - mk_shellcmdlist(v1); - free(v1); - } - } - - starttime = -1 ; - get_lua_number("texconfig", "start_time", &starttime); - if (starttime < 0) { - /* - We provide this one for compatibility reasons and therefore also in - uppercase. - */ - get_lua_number("texconfig", "SOURCE_DATE_EPOCH", &starttime); - } - if (starttime >= 0) { - set_start_time(starttime); - } - - utc = -1 ; - get_lua_boolean("texconfig", "use_utc_time", &utc); - if (utc >= 0 && utc <= 1) { - utc_option = utc; - } - - fix_dumpname(); - } else { - if (luainit) { - if (given_file) { - fprintf(stdout, "%s file %s not found\n", (lua_only ? "Script" : "Configuration"), given_file); - free(given_file); - } else { - fprintf(stdout, "No %s file given\n", (lua_only ? "script" : "configuration")); - } - exit(1); - } else { - /* init */ - init_kpse(); - fix_dumpname(); - } - } - /* Maybe we can extend this way to the others tables, using luac. */ - if ( safer_option || ((shellenabledp == 0) || (shellenabledp == 1 && restrictedshell == 1)) ) { - (void) luaL_dostring(Luas,"ffi=require[[ffi]]; for k,_ in pairs(ffi) do if k~='gc' then ffi[k]=nil end; end; ffi=nil;"); - } -} - -@ @c -void check_texconfig_init(void) -{ - if (Luas != NULL) { - lua_getglobal(Luas, "texconfig"); - if (lua_istable(Luas, -1)) { - lua_getfield(Luas, -1, "init"); - if (lua_isfunction(Luas, -1)) { - int i = lua_pcall(Luas, 0, 0, 0); - if (i != 0) { - /* Can't be more precise here, called before TeX initialization */ - fprintf(stderr, "This went wrong: %s\n", lua_tostring(Luas, -1)); - error(); - } - } - } - } -} +% luainit.w +% +% Copyright 2006-2017 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c + +#include "ptexlib.h" + +#include + +#include "lua/luatex-api.h" + +#include + +extern int load_luatex_core_lua (lua_State * L); + +/* internalized strings: see luatex-api.h */ +set_make_keys; + +@ +This file is getting a bit messy, but it is not simple to fix unilaterally. + +Better to wait until Karl has some time (after texlive 2008) so we can +synchronize with kpathsea. One problem, for instance, is that I would +like to resolve the full executable path. |kpse_set_program_name()| does +that, indirectly (by setting SELFAUTOLOC in the environment), but it +does much more, making it hard to use for our purpose. + +In fact, it sets three C variables: + + |kpse_invocation_name| |kpse_invocation_short_name| |kpse->program_name| + +and five environment variables: + + SELFAUTOLOC SELFAUTODIR SELFAUTOPARENT SELFAUTOGRANDPARENT progname + +@c +const_string LUATEX_IHELP[] = { + "Usage: " my_name " --lua=FILE [OPTION]... [TEXNAME[.tex]] [COMMANDS]", + " or: " my_name " --lua=FILE [OPTION]... \\FIRST-LINE", + " or: " my_name " --lua=FILE [OPTION]... &FMT ARGS", + " Run " MyName " on TEXNAME, usually creating TEXNAME.pdf.", + " Any remaining COMMANDS are processed as luatex input, after TEXNAME is read.", + "", + " Alternatively, if the first non-option argument begins with a backslash,", + " " my_name " interprets all non-option arguments as an input line.", + "", + " Alternatively, if the first non-option argument begins with a &, the", + " next word is taken as the FMT to read, overriding all else. Any", + " remaining arguments are processed as above.", + "", + " If no arguments or options are specified, prompt for input.", + "", + " The following regular options are understood: ", + "", + " --credits display credits and exit", + " --debug-format enable format debugging", + " --draftmode switch on draft mode (generates no output PDF)", + " --[no-]file-line-error disable/enable file:line:error style messages", + " --[no-]file-line-error-style aliases of --[no-]file-line-error", + " --fmt=FORMAT load the format file FORMAT", + " --halt-on-error stop processing at the first error", + " --help display help and exit", + " --ini be ini" my_name ", for dumping formats", + " --interaction=STRING set interaction mode (STRING=batchmode/nonstopmode/scrollmode/errorstopmode)", + " --jobname=STRING set the job name to STRING", + " --kpathsea-debug=NUMBER set path searching debugging flags according to the bits of NUMBER", + " --lua=FILE load and execute a lua initialization script", + " --[no-]mktex=FMT disable/enable mktexFMT generation (FMT=tex/tfm)", + " --nosocket disable the lua socket library", + " --output-comment=STRING use STRING for DVI file comment instead of date (no effect for PDF)", + " --output-directory=DIR use existing DIR as the directory to write files in", + " --output-format=FORMAT use FORMAT for job output; FORMAT is 'dvi' or 'pdf'", + " --progname=STRING set the program name to STRING", + " --recorder enable filename recorder", + " --safer disable easily exploitable lua commands", + " --[no-]shell-escape disable/enable system commands", + " --shell-restricted restrict system commands to a list of commands given in texmf.cnf", + " --synctex=NUMBER enable synctex", + " --utc init time to UTC", + " --version display version and exit", + "", + "Alternate behaviour models can be obtained by special switches", + "", + " --luaonly run a lua file, then exit", + " --luaconly byte-compile a lua file, then exit", + " --luahashchars the bits used by current Lua interpreter for strings hashing", +#ifdef LuajitTeX + " --jiton turns the JIT compiler on (default off)", + " --jithash=STRING choose the hash function for the lua strings (lua51|luajit20: default lua51)", +#endif + "", + "See the reference manual for more information about the startup process.", + NULL +}; + +@ Later we will put on environment |LC_CTYPE|, |LC_COLLATE| and +|LC_NUMERIC| set to |C|, so we need a place where to store the old values. +@c +const char *lc_ctype; +const char *lc_collate; +const char *lc_numeric; + +/* + " --8bit ignored, input is assumed to be in UTF-8 encoding", + " --default-translate-file=FILE ignored, input is assumed to be in UTF-8 encoding", + " --etex ignored, the etex extensions are always active", + " --disable-write18 disable \\write18{SHELL COMMAND}", + " --enable-write18 enable \\write18{SHELL COMMAND}", + " --[no-]parse-first-line ignored", + " --translate-file=FILE ignored, input is assumed to be in UTF-8 encoding", +*/ + +@ The return value will be the directory of the executable, e.g.: \.{c:/TeX/bin} +@c +static char *ex_selfdir(char *argv0) +{ +#if defined(WIN32) +#if defined(__MINGW32__) + char path[PATH_MAX], *fp; + + /* SearchPath() always gives back an absolute directory */ + if (SearchPath(NULL, argv0, ".exe", PATH_MAX, path, NULL) == 0) + FATAL1("Can't determine where the executable %s is.\n", argv0); + /* slashify the dirname */ + for (fp = path; fp && *fp; fp++) + if (IS_DIR_SEP(*fp)) + *fp = DIR_SEP; +#else /* __MINGW32__ */ +#define PATH_MAX 512 + char short_path[PATH_MAX], path[PATH_MAX], *fp; + + /* SearchPath() always gives back an absolute directory */ + if (SearchPath(NULL, argv0, ".exe", PATH_MAX, short_path, &fp) == 0) + FATAL1("Can't determine where the executable %s is.\n", argv0); + if (getlongpath(path, short_path, sizeof(path)) == 0) { + FATAL1("This path points to an invalid file : %s\n", short_path); + } +#endif /* __MINGW32__ */ + return xdirname(path); +#else /* WIN32 */ + return kpse_selfdir(argv0); +#endif +} + +@ @c +static void prepare_cmdline(lua_State * L, char **av, int ac, int zero_offset) +{ + int i; + char *s; + luaL_checkstack(L, ac + 3, "too many arguments to script"); + lua_createtable(L, 0, 0); + for (i = 0; i < ac; i++) { + lua_pushstring(L, av[i]); + lua_rawseti(L, -2, (i - zero_offset)); + } + lua_setglobal(L, "arg"); + lua_getglobal(L, "os"); + s = ex_selfdir(argv[0]); + lua_pushstring(L, s); + xfree(s); + lua_setfield(L, -2, "selfdir"); + return; +} + + +@ @c +int kpse_init = -1; + +@ @c +string input_name = NULL; + +static string user_progname = NULL; + +char *startup_filename = NULL; +int lua_only = 0; +int lua_offset = 0; +unsigned char show_luahashchars = 0; + +#ifdef LuajitTeX +int luajiton = 0; +char *jithash_hashname = NULL; +#endif + +int safer_option = 0; +int nosocket_option = 0; +int utc_option = 0; + +@ Reading the options. + +@ Test whether getopt found an option ``A''. +Assumes the option index is in the variable |option_index|, and the +option table in a variable |long_options|. + +@c +#define ARGUMENT_IS(a) STREQ (long_options[option_index].name, a) + +/* + SunOS cc can't initialize automatic structs, so make this static. +*/ + +/* + Nota Bene: we still intercept some options that other engines handle + so that existing scripted usage will not fail. +*/ + +static struct option long_options[] = { + {"fmt", 1, 0, 0}, + {"lua", 1, 0, 0}, + {"luaonly", 0, 0, 0}, + {"luahashchars", 0, 0, 0}, +#ifdef LuajitTeX + {"jiton", 0, 0, 0}, + {"jithash", 1, 0, 0}, +#endif + {"safer", 0, &safer_option, 1}, + {"utc", 0, &utc_option, 1}, + {"nosocket", 0, &nosocket_option, 1}, + {"help", 0, 0, 0}, + {"ini", 0, &ini_version, 1}, + {"interaction", 1, 0, 0}, + {"halt-on-error", 0, &haltonerrorp, 1}, + {"kpathsea-debug", 1, 0, 0}, + {"progname", 1, 0, 0}, + {"version", 0, 0, 0}, + {"credits", 0, 0, 0}, + {"recorder", 0, 0, 0}, + {"etex", 0, 0, 0}, + {"output-comment", 1, 0, 0}, + {"output-directory", 1, 0, 0}, + {"draftmode", 0, 0, 0}, + {"output-format", 1, 0, 0}, + {"shell-escape", 0, &shellenabledp, 1}, + {"no-shell-escape", 0, &shellenabledp, -1}, + {"enable-write18", 0, &shellenabledp, 1}, + {"disable-write18", 0, &shellenabledp, -1}, + {"shell-restricted", 0, 0, 0}, + {"debug-format", 0, &debug_format_file, 1}, + {"file-line-error-style", 0, &filelineerrorstylep, 1}, + {"no-file-line-error-style", 0, &filelineerrorstylep, -1}, + + /* Shorter option names for the above. */ + + {"file-line-error", 0, &filelineerrorstylep, 1}, + {"no-file-line-error", 0, &filelineerrorstylep, -1}, + {"jobname", 1, 0, 0}, + {"parse-first-line", 0, &parsefirstlinep, 1}, + {"no-parse-first-line", 0, &parsefirstlinep, -1}, + {"translate-file", 1, 0, 0}, + {"default-translate-file", 1, 0, 0}, + {"8bit", 0, 0, 0}, + {"mktex", 1, 0, 0}, + {"no-mktex", 1, 0, 0}, + + /* Synchronization: just like "interaction" above */ + + {"synctex", 1, 0, 0}, + {0, 0, 0, 0} +}; + +@ @c +int lua_numeric_field_by_index(lua_State * L, int name_index, int dflt) +{ + register int i = dflt; + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + if (lua_type(L, -1) == LUA_TNUMBER) { + i = lua_roundnumber(L, -1); + } + lua_pop(L, 1); + return i; +} + +@ @c +unsigned int lua_unsigned_numeric_field_by_index(lua_State * L, int name_index, int dflt) +{ + register unsigned int i = dflt; + lua_rawgeti(L, LUA_REGISTRYINDEX, name_index); /* fetch the stringptr */ + lua_rawget(L, -2); + if (lua_type(L, -1) == LUA_TNUMBER) { + i = lua_uroundnumber(L, -1); + } + lua_pop(L, 1); + return i; +} + +@ @c +static int recorderoption = 0; + +static void parse_options(int ac, char **av) +{ +#ifdef WIN32 +/* save argc and argv */ + int sargc = argc; + char **sargv = argv; +#endif + int g; /* `getopt' return code. */ + int option_index; + char *firstfile = NULL; + opterr = 0; /* dont whine */ +#ifdef LuajitTeX + if ((strstr(argv[0], "luajittexlua") != NULL) || + (strstr(argv[0], "texluajit") != NULL)) { +#else + if ((strstr(argv[0], "luatexlua") != NULL) || + (strstr(argv[0], "texlua") != NULL)) { +#endif + lua_only = 1; + luainit = 1; + } + + for (;;) { + g = getopt_long_only(ac, av, "+", long_options, &option_index); + if (g == -1) /* End of arguments, exit the loop. */ + break; + if (g == '?') { /* Unknown option. */ + if (!luainit) + fprintf(stderr,"%s: unrecognized option '%s'\n", argv[0], argv[optind-1]); + continue; + } + + assert(g == 0); /* We have no short option names. */ + + if (ARGUMENT_IS("luaonly")) { + lua_only = 1; + lua_offset = optind; + luainit = 1; + } else if (ARGUMENT_IS("lua")) { + startup_filename = optarg; + lua_offset = (optind - 1); + luainit = 1; +#ifdef LuajitTeX + } else if (ARGUMENT_IS("jiton")) { + luajiton = 1; + } else if (ARGUMENT_IS("jithash")) { + size_t len = strlen(optarg); + if (len<16) { + jithash_hashname = optarg; + } else { + WARNING2("hash name truncated to 15 characters from %d. (%s)", (int) len, optarg); + jithash_hashname = (string) xmalloc(16); + strncpy(jithash_hashname, optarg, 15); + jithash_hashname[15] = 0; + } +#endif + } else if (ARGUMENT_IS("luahashchars")) { + show_luahashchars = 1; + } else if (ARGUMENT_IS("kpathsea-debug")) { + kpathsea_debug |= atoi(optarg); + } else if (ARGUMENT_IS("progname")) { + user_progname = optarg; + } else if (ARGUMENT_IS("jobname")) { + c_job_name = optarg; + } else if (ARGUMENT_IS("fmt")) { + dump_name = optarg; + } else if (ARGUMENT_IS("output-directory")) { + output_directory = optarg; + } else if (ARGUMENT_IS("output-comment")) { + size_t len = strlen(optarg); + if (len < 256) { + output_comment = optarg; + } else { + WARNING2("Comment truncated to 255 characters from %d. (%s)", (int) len, optarg); + output_comment = (string) xmalloc(256); + strncpy(output_comment, optarg, 255); + output_comment[255] = 0; + } + } else if (ARGUMENT_IS("shell-restricted")) { + shellenabledp = 1; + restrictedshell = 1; + } else if (ARGUMENT_IS("output-format")) { + output_mode_option = 1; + if (strcmp(optarg, "dvi") == 0) { + output_mode_value = 0; + } else if (strcmp(optarg, "pdf") == 0) { + output_mode_value = 1; + } else { + WARNING1("Ignoring unknown value `%s' for --output-format",optarg); + output_mode_option = 0; + } + } else if (ARGUMENT_IS("draftmode")) { + draft_mode_option = 1; + draft_mode_value = 1; + } else if (ARGUMENT_IS("mktex")) { + kpse_maketex_option(optarg, true); + } else if (ARGUMENT_IS("no-mktex")) { + kpse_maketex_option(optarg, false); + } else if (ARGUMENT_IS("interaction")) { + /* These numbers match CPP defines */ + if (STREQ(optarg, "batchmode")) { + interactionoption = 0; + } else if (STREQ(optarg, "nonstopmode")) { + interactionoption = 1; + } else if (STREQ(optarg, "scrollmode")) { + interactionoption = 2; + } else if (STREQ(optarg, "errorstopmode")) { + interactionoption = 3; + } else { + WARNING1("Ignoring unknown argument `%s' to --interaction", optarg); + } + } else if (ARGUMENT_IS("synctex")) { + /* Synchronize TeXnology: catching the command line option as a long */ + synctexoption = (int) strtol(optarg, NULL, 0); + } else if (ARGUMENT_IS("recorder")) { + recorderoption = 1 ; + } else if (ARGUMENT_IS("help")) { + usagehelp(LUATEX_IHELP, BUG_ADDRESS); + } else if (ARGUMENT_IS("version")) { + print_version_banner(); + /* *INDENT-OFF* */ + puts("\n\nExecute '" my_name " --credits' for credits and version details.\n\n" + "There is NO warranty. Redistribution of this software is covered by\n" + "the terms of the GNU General Public License, version 2 or (at your option)\n" + "any later version. For more information about these matters, see the file\n" + "named COPYING and the LuaTeX source.\n\n" + "LuaTeX is Copyright 2017 Taco Hoekwater and the LuaTeX Team.\n"); + /* *INDENT-ON* */ + uexit(0); + } else if (ARGUMENT_IS("credits")) { + char *versions; + initversionstring(&versions); + print_version_banner(); + /* *INDENT-OFF* */ + puts("\n\nThe LuaTeX team is Hans Hagen, Hartmut Henkel, Taco Hoekwater, Luigi Scarso.\n\n" + MyName " merges and builds upon (parts of) the code from these projects:\n\n" + "tex : Donald Knuth\n" + "etex : Peter Breitenlohner, Phil Taylor and friends\n" + "omega : John Plaice and Yannis Haralambous\n" + "aleph : Giuseppe Bilotta\n" + "pdftex : Han The Thanh and friends\n" + "kpathsea : Karl Berry, Olaf Weber and others\n" + "lua : Roberto Ierusalimschy, Waldemar Celes and Luiz Henrique de Figueiredo\n" + "metapost : John Hobby, Taco Hoekwater and friends\n" + "poppler : Derek Noonburg, Kristian H\\ogsberg (partial)\n" + "fontforge : George Williams (partial)\n" + "luajit : Mike Pall (used in LuajitTeX)\n"); + /* *INDENT-ON* */ + puts(versions); + uexit(0); + } + } + /* attempt to find |input_name| / |dump_name| */ + if (lua_only) { + if (argv[optind]) { + startup_filename = xstrdup(argv[optind]); + lua_offset = optind; + } + } else if (argv[optind] && argv[optind][0] == '&') { + dump_name = xstrdup(argv[optind] + 1); + } else if (argv[optind] && argv[optind][0] != '\\') { + if (argv[optind][0] == '*') { + input_name = xstrdup(argv[optind] + 1); + } else { + firstfile = xstrdup(argv[optind]); + if ((strstr(firstfile, ".lua") == + firstfile + strlen(firstfile) - 4) + || (strstr(firstfile, ".luc") == + firstfile + strlen(firstfile) - 4) + || (strstr(firstfile, ".LUA") == + firstfile + strlen(firstfile) - 4) + || (strstr(firstfile, ".LUC") == + firstfile + strlen(firstfile) - 4)) { + if (startup_filename == NULL) { + startup_filename = firstfile; + lua_offset = optind; + lua_only = 1; + luainit = 1; + } + } else { + input_name = firstfile; + } + } +#ifdef WIN32 + } else if (sargc > 1 && sargv[sargc-1] && sargv[sargc-1][0] != '-' && + sargv[sargc-1][0] != '\\') { + if (sargv[sargc-1][0] == '&') + dump_name = xstrdup(sargv[sargc-1] + 1); + else { + if (sargv[sargc-1][0] == '*') + input_name = xstrdup(sargv[sargc-1] + 1); + else + input_name = xstrdup(sargv[sargc-1]); + sargv[sargc-1] = normalize_quotes(input_name, "argument"); + } + if (safer_option) /* --safer implies --nosocket */ + nosocket_option = 1; + return; +#endif + } + if (safer_option) /* --safer implies --nosocket */ + nosocket_option = 1; + /* Finalize the input filename. */ + if (input_name != NULL) { + argv[optind] = normalize_quotes(input_name, "argument"); + } +} + +@ test for readability +@c +#define is_readable(a) (stat(a,&finfo)==0) && S_ISREG(finfo.st_mode) && \ + (f=fopen(a,"r")) != NULL && !fclose(f) + +@ @c +static char *find_filename(char *name, const char *envkey) +{ + struct stat finfo; + char *dirname = NULL; + char *filename = NULL; + FILE *f; + if (is_readable(name)) { + return name; + } else { + dirname = getenv(envkey); + if ((dirname != NULL) && strlen(dirname)) { + dirname = xstrdup(getenv(envkey)); + if (*(dirname + strlen(dirname) - 1) == '/') { + *(dirname + strlen(dirname) - 1) = 0; + } + filename = xmalloc((unsigned) (strlen(dirname) + strlen(name) + 2)); + filename = concat3(dirname, "/", name); + xfree(dirname); + if (is_readable(filename)) { + return filename; + } + xfree(filename); + } + } + return NULL; +} + +@ @c + +static void init_kpse(void) +{ + if (!user_progname) { + user_progname = dump_name; + } else if (!dump_name) { + dump_name = user_progname; + } + if (!user_progname) { + if (ini_version) { + if (input_name) { + char *p = input_name + strlen(input_name) - 1; + while (p >= input_name) { + if (IS_DIR_SEP (*p)) { + p++; + input_name = p; + break; + } + p--; + } + user_progname = remove_suffix (input_name); + } + if (!user_progname) { + user_progname = kpse_program_basename(argv[0]); + } + } else { + if (!dump_name) { + dump_name = kpse_program_basename(argv[0]); + } + user_progname = dump_name; + } + } + kpse_set_program_enabled(kpse_fmt_format, MAKE_TEX_FMT_BY_DEFAULT, + kpse_src_compile); + + kpse_set_program_name(argv[0], user_progname); + init_shell_escape(); /* set up 'restrictedshell' */ + init_start_time(); + program_name_set = 1 ; + if (recorderoption) { + recorder_enabled = 1; + } +} + +@ @c +static void fix_dumpname(void) +{ + int dist; + if (dump_name) { + /* adjust array for Pascal and provide extension, if needed */ + dist = (int) (strlen(dump_name) - strlen(DUMP_EXT)); + if (strstr(dump_name, DUMP_EXT) == dump_name + dist) + TEX_format_default = dump_name; + else + TEX_format_default = concat(dump_name, DUMP_EXT); + } else { + /* For |dump_name| to be NULL is a bug. */ + if (!ini_version) { + fprintf(stdout, "no format given, quitting\n"); + exit(1); + } + } +} + +@ lua require patch + +@ Auxiliary function for kpse search + +@c +static const char *luatex_kpse_find_aux(lua_State *L, const char *name, + kpse_file_format_type format, const char *errname) +{ + const char *filename; + const char *altname; + altname = luaL_gsub(L, name, ".", "/"); /* Lua convention */ + filename = kpse_find_file(altname, format, false); + if (filename == NULL) { + filename = kpse_find_file(name, format, false); + } + if (filename == NULL) { + lua_pushfstring(L, "\n\t[kpse %s searcher] file not found: " LUA_QS, errname, name); + } + return filename; +} + +@ The lua search function. + +When kpathsea is not initialized, then it runs the +normal lua function that is saved in the registry, otherwise +it uses kpathsea. + +two registry ref variables are needed: one for the actual lua +function, the other for its environment . + +@c +static int lua_loader_function = 0; + +static int luatex_kpse_lua_find(lua_State * L) +{ + const char *filename; + const char *name; + name = luaL_checkstring(L, 1); + if (program_name_set == 0) { + lua_rawgeti(L, LUA_REGISTRYINDEX, lua_loader_function); + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + return 1; + } + filename = luatex_kpse_find_aux(L, name, kpse_lua_format, "lua"); + if (filename == NULL) + return 1; /* library not found in this path */ + if (luaL_loadfile(L, filename) != 0) { + luaL_error(L, "error loading module %s from file %s:\n\t%s", + lua_tostring(L, 1), filename, lua_tostring(L, -1)); + } + return 1; /* library loaded successfully */ +} + +@ @c +static int clua_loader_function = 0; +extern int searcher_C_luatex (lua_State *L, const char *name, const char *filename); + +static int luatex_kpse_clua_find(lua_State * L) +{ + const char *filename; + const char *name; + if (safer_option) { + lua_pushliteral(L, "\n\t[C searcher disabled in safer mode]"); + return 1; /* library not found in this path */ + } + name = luaL_checkstring(L, 1); + if (program_name_set == 0) { + lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function); + lua_pushvalue(L, -2); + lua_call(L, 1, 1); + return 1; + } else { + const char *path_saved; + char *prefix, *postfix, *p, *total; + char *extensionless; + char *temp_name; + int j; + filename = luatex_kpse_find_aux(L, name, kpse_clua_format, "C"); + if (filename == NULL) + return 1; /* library not found in this path */ + extensionless = strdup(filename); + if (!extensionless) + return 1; /* allocation failure */ + /* Fix Issue 850: replace '.' with LUA_DIRSEP */ + temp_name = strdup(name); + for(j=0; ; j++){ + if ((unsigned char)temp_name[j]=='\0') { + break; + } + if ((unsigned char)temp_name[j]=='.'){ + temp_name[j]=LUA_DIRSEP[0]; + } + } + p = strstr(extensionless, temp_name); + if (!p) return 1; /* this would be exceedingly weird */ + *p = '\0'; + prefix = strdup(extensionless); + if (!prefix) return 1; /* allocation failure */ + postfix = strdup(p+strlen(name)); + if (!postfix) return 1; /* allocation failure */ + total = malloc(strlen(prefix)+strlen(postfix)+2); + if (!total) return 1; /* allocation failure */ + snprintf(total,strlen(prefix)+strlen(postfix)+2, "%s?%s", prefix, postfix); + /* save package.path */ + lua_getglobal(L,"package"); + lua_getfield(L,-1,"cpath"); + path_saved = lua_tostring(L,-1); + lua_pop(L,1); + /* set package.path = "?" */ + lua_pushstring(L,total); + lua_setfield(L,-2,"cpath"); + lua_pop(L,1); /* pop "package" */ + /* run function */ + lua_rawgeti(L, LUA_REGISTRYINDEX, clua_loader_function); + lua_pushstring(L, name); + lua_call(L, 1, 1); + /* restore package.path */ + lua_getglobal(L,"package"); + lua_pushstring(L,path_saved); + lua_setfield(L,-2,"cpath"); + lua_pop(L,1); /* pop "package" */ + free(extensionless); + free(total); + free(temp_name); + return 1; + } +} + +@ Setting up the new search functions. + +This replaces package.searchers[2] and package.searchers[3] with the +functions defined above. + +@c +static void setup_lua_path(lua_State * L) +{ + lua_getglobal(L, "package"); +#ifdef LuajitTeX + lua_getfield(L, -1, "loaders"); +#else + lua_getfield(L, -1, "searchers"); +#endif + lua_rawgeti(L, -1, 2); /* package.searchers[2] */ + lua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushcfunction(L, luatex_kpse_lua_find); + lua_rawseti(L, -2, 2); /* replace the normal lua loader */ + + lua_rawgeti(L, -1, 3); /* package.searchers[3] */ + clua_loader_function = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushcfunction(L, luatex_kpse_clua_find); + lua_rawseti(L, -2, 3); /* replace the normal lua lib loader */ + + lua_pop(L, 2); /* pop the array and table */ +} + +@ helper variables for the safe keeping of table ids + +@c +int tex_table_id; +int pdf_table_id; +int token_table_id; +int node_table_id; + +@ @c +int l_pack_type_index [PACK_TYPE_SIZE] ; +int l_group_code_index [GROUP_CODE_SIZE]; +int l_local_par_index [LOCAL_PAR_SIZE]; +int l_math_style_name_index [MATH_STYLE_NAME_SIZE]; +int l_dir_par_index [DIR_PAR_SIZE]; +int l_dir_text_index [DIR_TEXT_SIZE]; + +int img_parms [img_parms_max]; +int img_pageboxes [img_pageboxes_max]; + +int lua_show_valid_list(lua_State *L, const char **list, int max) +{ + int i; + lua_newtable(L); + for (i = 0; i < max; i++) { + lua_pushinteger(L,i+1); + lua_pushstring(L, list[i]); + lua_settable(L, -3); + } + return 1; +} + +int lua_show_valid_keys(lua_State *L, int *list, int max) +{ + int i; + lua_newtable(L); + for (i = 0; i < max; i++) { + lua_pushinteger(L,i+1); + lua_rawgeti(L, LUA_REGISTRYINDEX, list[i]); + lua_settable(L, -3); + } + return 1; +} + +#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) +char **suffixlist; + +# define EXE_SUFFIXES ".com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh;.ws;.tcl;.py;.pyw" + +@ @c +static void mk_suffixlist(void) +{ + char **p; + char *q, *r, *v; + int n; + +# if defined(__CYGWIN__) + v = xstrdup(EXE_SUFFIXES); +# else + v = (char *) getenv("PATHEXT"); + if (v) /* strlwr() exists also in MingW */ + v = (char *) strlwr(xstrdup(v)); + else + v = xstrdup(EXE_SUFFIXES); +# endif + + q = v; + n = 0; + + while ((r = strchr(q, ';')) != NULL) { + n++; + r++; + q = r; + } + if (*q) + n++; + suffixlist = (char **) xmalloc((n + 2) * sizeof(char *)); + p = suffixlist; + *p = xstrdup(".dll"); + p++; + q = v; + while ((r = strchr(q, ';')) != NULL) { + *r = '\0'; + *p = xstrdup(q); + p++; + r++; + q = r; + } + if (*q) { + *p = xstrdup(q); + p++; + } + *p = NULL; + free(v); +} +#endif + +@ @c +void lua_initialize(int ac, char **av) +{ + char *given_file = NULL; + char *banner; + /*int kpse_init;*/ + size_t len; + int starttime; + int utc; + static char LC_CTYPE_C[] = "LC_CTYPE=C"; + static char LC_COLLATE_C[] = "LC_COLLATE=C"; + static char LC_NUMERIC_C[] = "LC_NUMERIC=C"; + static char engine_luatex[] = "engine=" my_name; + char *old_locale = NULL; + char *env_locale = NULL; + char *tmp = NULL; + /* Save to pass along to topenin. */ + const char *fmt = "This is " MyName ", Version %s" WEB2CVERSION; + argc = ac; + argv = av; + len = strlen(fmt) + strlen(luatex_version_string) ; + banner = xmalloc(len); + sprintf(banner, fmt, luatex_version_string); + luatex_banner = banner; + kpse_invocation_name = kpse_program_basename(argv[0]); + + /* be 'luac' */ + if (argc >1) { +#ifdef LuajitTeX + if (FILESTRCASEEQ(kpse_invocation_name, "texluajitc")) + exit(luac_main(ac, av)); + if (STREQ(argv[1], "--luaconly") || STREQ(argv[1], "--luac")) { + char *argv1 = xmalloc (strlen ("luajittex") + 1); + av[1] = argv1; + strcpy (av[1], "luajittex"); + exit(luac_main(--ac, ++av)); + } +#else + if (FILESTRCASEEQ(kpse_invocation_name, "texluac")) + exit(luac_main(ac, av)); + if (STREQ(argv[1], "--luaconly") || STREQ(argv[1], "--luac")) { + strcpy(av[1], "luatex"); + exit(luac_main(--ac, ++av)); + } +#endif + } +#if defined(WIN32) || defined(__MINGW32__) || defined(__CYGWIN__) + mk_suffixlist(); +#endif + + /* Must be initialized before options are parsed. */ + interactionoption = 4; + dump_name = NULL; + + /* 0 means "disable Synchronize TeXnology". + synctexoption is a *.web variable. + We initialize it to a weird value to catch the -synctex command line flag + At runtime, if synctexoption is not |INT_MAX|, then it contains the command line option provided, + otherwise no such option was given by the user. */ +#define SYNCTEX_NO_OPTION INT_MAX + synctexoption = SYNCTEX_NO_OPTION; + + /* parse commandline */ + parse_options(ac, av); + if (lua_only) { + /* Shell has no restrictions. */ + shellenabledp = true; + restrictedshell = false; + safer_option = 0; + } + /* Get the current locale (it should be C ) */ + /* and save LC_CTYPE, LC_COLLATE and LC_NUMERIC. */ + /* Later luainterpreter() will consciously use them. */ + old_locale = setlocale (LC_ALL, NULL); + lc_ctype = NULL; + lc_collate = NULL; + lc_numeric = NULL; + if (old_locale) { + /* If setlocale fails here, then the state */ + /* could be compromised, and we exit. */ + env_locale = setlocale (LC_ALL, ""); + if (!env_locale) { + fprintf(stderr,"Unable to read environment locale:exit now.\n"); + exit(1); + } + tmp = setlocale (LC_CTYPE, NULL); + if (tmp) { + lc_ctype = xstrdup(tmp); + } + tmp = setlocale (LC_COLLATE, NULL); + if (tmp){ + lc_collate = xstrdup(tmp); + } + tmp = setlocale (LC_NUMERIC, NULL); + if (tmp){ + lc_numeric = xstrdup(tmp); + } + /* Back to the previous locale if possible, */ + /* otherwise it's a serious error and we exit:*/ + /* we can't ensure a 'sane' locale for lua. */ + env_locale = setlocale (LC_ALL, old_locale); + if (!env_locale) { + fprintf(stderr,"Unable to restore original locale:exit now.\n"); + exit(1); + } + } else { + fprintf(stderr,"Unable to store environment locale.\n"); + } + + /* make sure that the locale is 'sane' (for lua) */ + putenv(LC_CTYPE_C); + putenv(LC_COLLATE_C); + putenv(LC_NUMERIC_C); + + /* this is sometimes needed */ + putenv(engine_luatex); + + luainterpreter(); + + /* init internalized strings */ + set_init_keys; + + lua_pushstring(Luas,"lua.functions"); + lua_newtable(Luas); + lua_settable(Luas,LUA_REGISTRYINDEX); + + /* here start the key definitions */ + set_l_pack_type_index; + set_l_group_code_index; + set_l_local_par_index; + set_l_math_style_name_index; + set_l_dir_par_index; + set_l_dir_text_index; + + set_l_img_keys_index; + set_l_img_pageboxes_index; + + prepare_cmdline(Luas, argv, argc, lua_offset); /* collect arguments */ + setup_lua_path(Luas); + + if (startup_filename != NULL) { + given_file = xstrdup(startup_filename); + if (lua_only) { + xfree(startup_filename); + } + startup_filename = find_filename(given_file, "LUATEXDIR"); + } + /* now run the file */ + if (startup_filename != NULL) { + char *v1; + /* hide the 'tex' and 'pdf' table */ + tex_table_id = hide_lua_table(Luas, "tex"); + token_table_id = hide_lua_table(Luas, "token"); + node_table_id = hide_lua_table(Luas, "node"); + pdf_table_id = hide_lua_table(Luas, "pdf"); + + if (luaL_loadfile(Luas, startup_filename)) { + fprintf(stdout, "%s\n", lua_tostring(Luas, -1)); + exit(1); + } + /* */ + init_tex_table(Luas); + if (lua_pcall(Luas, 0, 0, 0)) { + fprintf(stdout, "%s\n", lua_tostring(Luas, -1)); + lua_traceback(Luas); + exit(1); + } + /* no filename? quit now! */ + if (!input_name) { + get_lua_string("texconfig", "jobname", &input_name); + } + if (!dump_name) { + get_lua_string("texconfig", "formatname", &dump_name); + } + if (lua_only) { + if (given_file) + free(given_file); + /* this is not strictly needed but it pleases valgrind */ + lua_close(Luas); + exit(0); + } + /* unhide the 'tex' and 'pdf' table */ + unhide_lua_table(Luas, "tex", tex_table_id); + unhide_lua_table(Luas, "pdf", pdf_table_id); + unhide_lua_table(Luas, "token", token_table_id); + unhide_lua_table(Luas, "node", node_table_id); + + /* |kpse_init| */ + kpse_init = -1; + get_lua_boolean("texconfig", "kpse_init", &kpse_init); + + if (kpse_init != 0) { + luainit = 0; /* re-enable loading of texmf.cnf values, see luatex.ch */ + init_kpse(); + kpse_init = 1; + } + /* |prohibit_file_trace| (boolean) */ + tracefilenames = 1; + get_lua_boolean("texconfig", "trace_file_names", &tracefilenames); + + /* |file_line_error| */ + filelineerrorstylep = false; + get_lua_boolean("texconfig", "file_line_error", &filelineerrorstylep); + + /* |halt_on_error| */ + haltonerrorp = false; + get_lua_boolean("texconfig", "halt_on_error", &haltonerrorp); + + /* |restrictedshell| */ + v1 = NULL; + get_lua_string("texconfig", "shell_escape", &v1); + if (v1) { + if (*v1 == 't' || *v1 == 'y' || *v1 == '1') { + shellenabledp = 1; + } else if (*v1 == 'p') { + shellenabledp = 1; + restrictedshell = 1; + } + free(v1); + } + /* If shell escapes are restricted, get allowed cmds from cnf. */ + if (shellenabledp && restrictedshell == 1) { + v1 = NULL; + get_lua_string("texconfig", "shell_escape_commands", &v1); + if (v1) { + mk_shellcmdlist(v1); + free(v1); + } + } + + starttime = -1 ; + get_lua_number("texconfig", "start_time", &starttime); + if (starttime < 0) { + /* + We provide this one for compatibility reasons and therefore also in + uppercase. + */ + get_lua_number("texconfig", "SOURCE_DATE_EPOCH", &starttime); + } + if (starttime >= 0) { + set_start_time(starttime); + } + + utc = -1 ; + get_lua_boolean("texconfig", "use_utc_time", &utc); + if (utc >= 0 && utc <= 1) { + utc_option = utc; + } + + fix_dumpname(); + } else { + if (luainit) { + if (given_file) { + fprintf(stdout, "%s file %s not found\n", (lua_only ? "Script" : "Configuration"), given_file); + free(given_file); + } else { + fprintf(stdout, "No %s file given\n", (lua_only ? "script" : "configuration")); + } + exit(1); + } else { + /* init */ + init_kpse(); + kpse_init = 1; + fix_dumpname(); + } + } + + /* Here we load luatex-core.lua which takes care of some protection on demand. */ + if (load_luatex_core_lua(Luas)) + fprintf(stderr, "Error in execution of luatex-core.lua .\n"); + /* Done. */ +} + +@ @c +void check_texconfig_init(void) +{ + if (Luas != NULL) { + lua_getglobal(Luas, "texconfig"); + if (lua_istable(Luas, -1)) { + lua_getfield(Luas, -1, "init"); + if (lua_isfunction(Luas, -1)) { + int i = lua_pcall(Luas, 0, 0, 0); + if (i != 0) { + /* Can't be more precise here, called before TeX initialization */ + fprintf(stderr, "This went wrong: %s\n", lua_tostring(Luas, -1)); + error(); + } + } + } + } +} diff --git a/Build/source/texk/web2c/luatexdir/lua/luastuff.w b/Build/source/texk/web2c/luatexdir/lua/luastuff.w index d9743ba4f21..6f57e8ee845 100644 --- a/Build/source/texk/web2c/luatexdir/lua/luastuff.w +++ b/Build/source/texk/web2c/luatexdir/lua/luastuff.w @@ -1,563 +1,679 @@ -% luastuff.w -% -% Copyright 2006-2013 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c -#include "ptexlib.h" -#include "lua/luatex-api.h" - -@ @c -lua_State *Luas = NULL; - -int luastate_bytes = 0; -int lua_active = 0; - -@ @c -void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, - const char *setfunc) -{ - /* make the table *//* |[{}]| */ - lua_pushstring(L, tab); /* |[{},"dimen"]| */ - lua_newtable(L); /* |[{},"dimen",{}]| */ - lua_settable(L, -3); /* |[{}]| */ - /* fetch it back */ - lua_pushstring(L, tab); /* |[{},"dimen"]| */ - lua_gettable(L, -2); /* |[{},{}]| */ - /* make the meta entries */ - luaL_newmetatable(L, mttab); /* |[{},{},{}]| */ - lua_pushstring(L, "__index"); /* |[{},{},{},"__index"]| */ - lua_pushstring(L, getfunc); /* |[{},{},{},"__index","getdimen"]| */ - lua_gettable(L, -5); /* |[{},{},{},"__index",]| */ - lua_settable(L, -3); /* |[{},{},{}]| */ - lua_pushstring(L, "__newindex"); /* |[{},{},{},"__newindex"]| */ - lua_pushstring(L, setfunc); /* |[{},{},{},"__newindex","setdimen"]| */ - lua_gettable(L, -5); /* |[{},{},{},"__newindex",]| */ - lua_settable(L, -3); /* |[{},{},{}]| */ - lua_setmetatable(L, -2); /* |[{},{}]| : assign the metatable */ - lua_pop(L, 1); /* |[{}]| : clean the stack */ -} - -@ @c -static const char *getS(lua_State * L, void *ud, size_t * size) -{ - LoadS *ls = (LoadS *) ud; - (void) L; - if (ls->size == 0) - return NULL; - *size = ls->size; - ls->size = 0; - return ls->s; -} - -@ @c -static void *my_luaalloc(void *ud, void *ptr, size_t osize, size_t nsize) -{ - void *ret = NULL; - (void) ud; /* for -Wunused */ - if (nsize == 0) - free(ptr); - else - ret = realloc(ptr, nsize); - luastate_bytes += (int) (nsize - osize); - return ret; -} - -@ @c -static int my_luapanic(lua_State * L) -{ - (void) L; /* to avoid warnings */ - fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1)); - return 0; -} - -@ @c -void luafunctioncall(int slot) -{ - int i ; - int stacktop = lua_gettop(Luas); - lua_active++; - lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(lua_functions)); - lua_gettable(Luas, LUA_REGISTRYINDEX); - lua_rawgeti(Luas, -1,slot); - if (lua_isfunction(Luas,-1)) { - int base = lua_gettop(Luas); /* function index */ - lua_pushinteger(Luas, slot); - lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ - lua_insert(Luas, base); /* put it under chunk */ - i = lua_pcall(Luas, 1, 0, base); - lua_remove(Luas, base); /* remove traceback function */ - if (i != 0) { - lua_gc(Luas, LUA_GCCOLLECT, 0); - Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); - } - } - lua_settop(Luas,stacktop); - lua_active--; -} - -@ @c -static const luaL_Reg lualibs[] = { - {"", luaopen_base}, - {"package", luaopen_package}, - {"coroutine", luaopen_coroutine}, - {"table", luaopen_table}, - {"io", open_iolibext}, - {"os", luaopen_os}, - {"string", luaopen_string}, - {"math", luaopen_math}, - {"debug", luaopen_debug}, - {"unicode", luaopen_unicode}, - {"zip", luaopen_zip}, - {"bit32", luaopen_bit32}, - {"md5", luaopen_md5}, - {"ffi", luaopen_ffi}, - {"lfs", luaopen_lfs}, - {"profiler", luaopen_profiler}, - {"lpeg", luaopen_lpeg}, - {NULL, NULL} -}; - -@ @c -static void do_openlibs(lua_State * L) -{ - const luaL_Reg *lib; - for (lib = lualibs; lib->func; lib++) { - luaL_requiref(L, lib->name, lib->func, 1); - lua_pop(L, 1); /* remove lib */ - } -} - -@ @c -static int load_aux (lua_State *L, int status) { - if (status == 0) /* OK? */ - return 1; - else { - lua_pushnil(L); - lua_insert(L, -2); /* put before error message */ - return 2; /* return nil plus error message */ - } -} - -@ @c -static int luatex_loadfile (lua_State *L) { - int status = 0; - const char *fname = luaL_optstring(L, 1, NULL); - const char *mode = luaL_optstring(L, 2, NULL); - int env = !lua_isnone(L, 3); /* 'env' parameter? */ - if (!lua_only && !fname && interaction == batch_mode) { - lua_pushnil(L); - lua_pushstring(L, "reading from stdin is disabled in batch mode"); - return 2; /* return nil plus error message */ - } - status = luaL_loadfilex(L, fname, mode); - if (status == LUA_OK) { - recorder_record_input(fname); - if (env) { /* 'env' parameter? */ - lua_pushvalue(L, 3); - lua_setupvalue(L, -2, 1); /* set it as 1st upvalue of loaded chunk */ - } - } - return load_aux(L, status); -} - -@ @c -static int luatex_dofile (lua_State *L) { - const char *fname = luaL_optstring(L, 1, NULL); - int n = lua_gettop(L); - if (!lua_only && !fname) { - if (interaction == batch_mode) { - lua_pushnil(L); - lua_pushstring(L, "reading from stdin is disabled in batch mode"); - return 2; /* return nil plus error message */ - } else { - tprint_nl("lua> "); - } - } - if (luaL_loadfile(L, fname) != 0) lua_error(L); - recorder_record_input(fname); - lua_call(L, 0, LUA_MULTRET); - return lua_gettop(L) - n; -} - -@ @c -void luainterpreter(void) -{ - lua_State *L; - L = lua_newstate(my_luaalloc, NULL); - if (L == NULL) { - fprintf(stderr, "Can't create the Lua state.\n"); - return; - } - lua_atpanic(L, &my_luapanic); - - do_openlibs(L); /* does all the 'simple' libraries */ - - lua_pushcfunction(L,luatex_dofile); - lua_setglobal(L, "dofile"); - lua_pushcfunction(L,luatex_loadfile); - lua_setglobal(L, "loadfile"); - - luatex_md5_lua_open(L); - - open_oslibext(L, safer_option); -/* - open_iolibext(L); -*/ - open_strlibext(L); - open_lfslibext(L); - - - /* luasockets */ - /* socket and mime are a bit tricky to open because - they use a load-time dependency that has to be - worked around for luatex, where the C module is - loaded way before the lua module. - */ - if (!nosocket_option) { - lua_getglobal(L, "package"); - lua_getfield(L, -1, "loaded"); - if (!lua_istable(L, -1)) { - lua_newtable(L); - lua_setfield(L, -2, "loaded"); - lua_getfield(L, -1, "loaded"); - } - luaopen_socket_core(L); - lua_setfield(L, -2, "socket.core"); - lua_pushnil(L); - lua_setfield(L, -2, "socket"); /* package.loaded.socket = nil */ - - luaopen_mime_core(L); - lua_setfield(L, -2, "mime.core"); - lua_pushnil(L); - lua_setfield(L, -2, "mime"); /* package.loaded.mime = nil */ - lua_pop(L, 2); /* pop the tables */ - - luatex_socketlua_open(L); /* preload the pure lua modules */ - } - /* zlib. slightly odd calling convention */ - luaopen_zlib(L); - lua_setglobal(L, "zlib"); - luaopen_gzip(L); - - /* our own libraries */ - luaopen_ff(L); - luaopen_tex(L); - luaopen_token(L); - luaopen_node(L); - luaopen_texio(L); - luaopen_kpse(L); - luaopen_callback(L); - luaopen_lua(L, startup_filename); - luaopen_stats(L); - luaopen_font(L); - luaopen_lang(L); - luaopen_mplib(L); - luaopen_vf(L); - - /* |luaopen_pdf(L);| */ - /* environment table at |LUA_ENVIRONINDEX| needs to load this way: */ - lua_pushcfunction(L, luaopen_pdf); - lua_pushstring(L, "pdf"); - lua_call(L, 1, 0); - - if (!lua_only) { - luaL_requiref(L, "img", luaopen_img, 1); - lua_pop(L, 1); - } - - luaL_requiref(L, "epdf", luaopen_epdf, 1); - lua_pop(L, 1); - - /* |luaopen_pdfscanner(L);| */ - lua_pushcfunction(L, luaopen_pdfscanner); - lua_pushstring(L, "pdfscanner"); - lua_call(L, 1, 0); - - lua_createtable(L, 0, 0); - lua_setglobal(L, "texconfig"); - - if (safer_option) { - /* disable some stuff if --safer */ - (void) hide_lua_value(L, "os", "execute"); - (void) hide_lua_value(L, "os", "rename"); - (void) hide_lua_value(L, "os", "remove"); - (void) hide_lua_value(L, "io", "popen"); - /* make io.open only read files */ - luaL_checkstack(L, 2, "out of stack space"); - lua_getglobal(L, "io"); - lua_getfield(L, -1, "open_ro"); - lua_setfield(L, -2, "open"); - (void) hide_lua_value(L, "io", "tmpfile"); - (void) hide_lua_value(L, "io", "output"); - (void) hide_lua_value(L, "lfs", "chdir"); - (void) hide_lua_value(L, "lfs", "lock"); - (void) hide_lua_value(L, "lfs", "touch"); - (void) hide_lua_value(L, "lfs", "rmdir"); - (void) hide_lua_value(L, "lfs", "mkdir"); - } - Luas = L; -} - -@ @c -int hide_lua_table(lua_State * L, const char *name) -{ - int r = 0; - lua_getglobal(L, name); - if (lua_istable(L, -1)) { - r = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushnil(L); - lua_setglobal(L, name); - } - return r; -} - -@ @c -void unhide_lua_table(lua_State * L, const char *name, int r) -{ - lua_rawgeti(L, LUA_REGISTRYINDEX, r); - lua_setglobal(L, name); - luaL_unref(L, LUA_REGISTRYINDEX, r); -} - -@ @c -int hide_lua_value(lua_State * L, const char *name, const char *item) -{ - int r = 0; - lua_getglobal(L, name); - if (lua_istable(L, -1)) { - lua_getfield(L, -1, item); - r = luaL_ref(L, LUA_REGISTRYINDEX); - lua_pushnil(L); - lua_setfield(L, -2, item); - } - return r; -} - -@ @c -void unhide_lua_value(lua_State * L, const char *name, const char *item, int r) -{ - lua_getglobal(L, name); - if (lua_istable(L, -1)) { - lua_rawgeti(L, LUA_REGISTRYINDEX, r); - lua_setfield(L, -2, item); - luaL_unref(L, LUA_REGISTRYINDEX, r); - } -} - -@ @c -int lua_traceback(lua_State * L) -{ - lua_getglobal(L, "debug"); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - return 1; - } - lua_getfield(L, -1, "traceback"); - if (!lua_isfunction(L, -1)) { - lua_pop(L, 2); - return 1; - } - lua_pushvalue(L, 1); /* pass error message */ - lua_pushinteger(L, 2); /* skip this function and traceback */ - lua_call(L, 2, 1); /* call debug.traceback */ - return 1; -} - -@ @c -static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized lua_id resolving */ -{ - LoadS ls; - int i; - size_t ll = 0; - char *lua_id; - char *s = NULL; - - if (Luas == NULL) { - luainterpreter(); - } - lua_active++; - if (is_string) { - const char *ss = NULL; - lua_rawgeti(Luas, LUA_REGISTRYINDEX, p); - if (lua_isfunction(Luas,-1)) { - int base = lua_gettop(Luas); /* function index */ - lua_checkstack(Luas, 1); - lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ - lua_insert(Luas, base); /* put it under chunk */ - i = lua_pcall(Luas, 0, 0, base); - lua_remove(Luas, base); /* remove traceback function */ - if (i != 0) { - lua_gc(Luas, LUA_GCCOLLECT, 0); - Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); - } - lua_active--; - return ; - } - ss = lua_tolstring(Luas, -1, &ll); - s = xmalloc(ll+1); - memcpy(s,ss,ll+1); - lua_pop(Luas,1); - } else { - int l = 0; - s = tokenlist_to_cstring(p, 1, &l); - ll = (size_t)l; - } - ls.s = s; - ls.size = ll; - if (ls.size > 0) { - if (nameptr > 0) { - int l = 0; /* not used */ - lua_id = tokenlist_to_cstring(nameptr, 1, &l); - i = lua_load(Luas, getS, &ls, lua_id, NULL); - xfree(lua_id); - } else if (nameptr < 0) { - lua_id = get_lua_name((nameptr + 65536)); - if (lua_id != NULL) { - i = lua_load(Luas, getS, &ls, lua_id, NULL); - } else { - i = lua_load(Luas, getS, &ls, "=[\\latelua]", NULL); - } - } else { - i = lua_load(Luas, getS, &ls, "=[\\latelua]", NULL); - } - if (i != 0) { - Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); - } else { - int base = lua_gettop(Luas); /* function index */ - lua_checkstack(Luas, 1); - lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ - lua_insert(Luas, base); /* put it under chunk */ - i = lua_pcall(Luas, 0, 0, base); - lua_remove(Luas, base); /* remove traceback function */ - if (i != 0) { - lua_gc(Luas, LUA_GCCOLLECT, 0); - Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); - } - } - xfree(ls.s); - } - lua_active--; -} - -@ @c -void late_lua(PDF pdf, halfword p) -{ - (void) pdf; - if (late_lua_type(p)==normal) { - expand_macros_in_tokenlist(p); /* sets |def_ref| */ - luacall(def_ref, late_lua_name(p), false); - flush_list(def_ref); - } else { - luacall(late_lua_data(p), late_lua_name(p), true); - } -} - -@ @c -void luatokencall(int p, int nameptr) /* hh-ls: optimized lua_id resolving */ -{ - LoadS ls; - int i, l; - char *s = NULL; - char *lua_id; - assert(Luas); - l = 0; - lua_active++; - s = tokenlist_to_cstring(p, 1, &l); - ls.s = s; - ls.size = (size_t) l; - if (ls.size > 0) { - if (nameptr > 0) { - lua_id = tokenlist_to_cstring(nameptr, 1, &l); - i = lua_load(Luas, getS, &ls, lua_id, NULL); - xfree(lua_id); - } else if (nameptr < 0) { - lua_id = get_lua_name((nameptr + 65536)); - if (lua_id != NULL) { - i = lua_load(Luas, getS, &ls, lua_id, NULL); - } else { - i = lua_load(Luas, getS, &ls, "=[\\directlua]", NULL); - } - } else { - i = lua_load(Luas, getS, &ls, "=[\\directlua]", NULL); - } - xfree(s); - if (i != 0) { - Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); - } else { - int base = lua_gettop(Luas); /* function index */ - lua_checkstack(Luas, 1); - lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ - lua_insert(Luas, base); /* put it under chunk */ - i = lua_pcall(Luas, 0, 0, base); - lua_remove(Luas, base); /* remove traceback function */ - if (i != 0) { - lua_gc(Luas, LUA_GCCOLLECT, 0); - Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); - } - } - } - lua_active--; -} - -@ @c -lua_State *luatex_error(lua_State * L, int is_fatal) -{ - - const_lstring luaerr; - char *err = NULL; - if (lua_type(L, -1) == LUA_TSTRING) { - luaerr.s = lua_tolstring(L, -1, &luaerr.l); - /* free last one ? */ - err = (char *) xmalloc((unsigned) (luaerr.l + 1)); - snprintf(err, (luaerr.l + 1), "%s", luaerr.s); - last_lua_error = err; /* hm, what if we have several .. not freed */ - } - if (is_fatal > 0) { - /* Normally a memory error from lua. - The pool may overflow during the |maketexlstring()|, but we - are crashing anyway so we may as well abort on the pool size */ - normal_error("lua",err); - /* never reached */ - lua_close(L); - return (lua_State *) NULL; - } else { - normal_warning("lua",err); - return L; - } -} - -@ @c -void preset_environment(lua_State * L, const parm_struct * p, const char *s) -{ - int i; - assert(L != NULL); - /* double call with same s gives assert(0) */ - lua_pushstring(L, s); /* s */ - lua_gettable(L, LUA_REGISTRYINDEX); /* t */ - assert(lua_isnil(L, -1)); - lua_pop(L, 1); /* - */ - lua_pushstring(L, s); /* s */ - lua_newtable(L); /* t s */ - for (i = 1, ++p; p->name != NULL; i++, p++) { - assert(i == p->idx); - lua_pushstring(L, p->name); /* k t s */ - lua_pushinteger(L, p->idx); /* v k t s */ - lua_settable(L, -3); /* t s */ - } - lua_settable(L, LUA_REGISTRYINDEX); /* - */ -} +% luastuff.w +% +% Copyright 2006-2013 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c +#include "ptexlib.h" +#include "lua/luatex-api.h" +#ifdef LuajitTeX +#include "lua/lauxlib_bridge.h" +#endif + +@ @c +lua_State *Luas = NULL; + +int luastate_bytes = 0; +int lua_active = 0; + +#ifdef LuajitTeX +#define Luas_load(Luas,getS,ls,lua_id) \ + lua_load(Luas,getS,ls,lua_id); +#define Luas_open(name,luaopen_lib) \ + lua_pushcfunction(L, luaopen_lib); \ + lua_pushstring(L, name); \ + lua_call(L, 1, 0); +#else +#define Luas_load(Luas,getS,ls,lua_id) \ + lua_load(Luas,getS,ls,lua_id,NULL); +#define Luas_open(name,luaopen_lib) \ + luaL_requiref(L, name, luaopen_lib, 1); \ + lua_pop(L, 1); +#endif + +@ @c +void make_table(lua_State * L, const char *tab, const char *mttab, const char *getfunc, const char *setfunc) +{ + /* make the table *//* |[{}]| */ + lua_pushstring(L, tab); /* |[{},"dimen"]| */ + lua_newtable(L); /* |[{},"dimen",{}]| */ + lua_settable(L, -3); /* |[{}]| */ + /* fetch it back */ + lua_pushstring(L, tab); /* |[{},"dimen"]| */ + lua_gettable(L, -2); /* |[{},{}]| */ + /* make the meta entries */ + luaL_newmetatable(L, mttab); /* |[{},{},{}]| */ + lua_pushstring(L, "__index"); /* |[{},{},{},"__index"]| */ + lua_pushstring(L, getfunc); /* |[{},{},{},"__index","getdimen"]| */ + lua_gettable(L, -5); /* |[{},{},{},"__index",]| */ + lua_settable(L, -3); /* |[{},{},{}]| */ + lua_pushstring(L, "__newindex"); /* |[{},{},{},"__newindex"]| */ + lua_pushstring(L, setfunc); /* |[{},{},{},"__newindex","setdimen"]| */ + lua_gettable(L, -5); /* |[{},{},{},"__newindex",]| */ + lua_settable(L, -3); /* |[{},{},{}]| */ + lua_setmetatable(L, -2); /* |[{},{}]| : assign the metatable */ + lua_pop(L, 1); /* |[{}]| : clean the stack */ +} + +@ @c +static const char *getS(lua_State * L, void *ud, size_t * size) +{ + LoadS *ls = (LoadS *) ud; + (void) L; + if (ls->size == 0) + return NULL; + *size = ls->size; + ls->size = 0; + return ls->s; +} + +@ @c +#ifdef LuajitTeX + /* Luatex has its own memory allocator, LuajitTeX uses the */ + /* standard one from the stock. We left this space as */ + /* reference, but be careful: memory allocator is a key */ + /* component in luajit, it's easy to get sub-optimal */ + /* performances. */ +#else +static void *my_luaalloc(void *ud, void *ptr, size_t osize, size_t nsize) +{ + void *ret = NULL; + (void) ud; /* for -Wunused */ + if (nsize == 0) + free(ptr); + else + ret = realloc(ptr, nsize); + luastate_bytes += (int) (nsize - osize); + return ret; +} +#endif + +@ @c +static int my_luapanic(lua_State * L) +{ + (void) L; /* to avoid warnings */ + fprintf(stderr, "PANIC: unprotected error in call to Lua API (%s)\n", lua_tostring(L, -1)); + return 0; +} + +@ @c +void luafunctioncall(int slot) +{ + int i ; + int stacktop = lua_gettop(Luas); + lua_active++; + lua_rawgeti(Luas, LUA_REGISTRYINDEX, lua_key_index(lua_functions)); + lua_gettable(Luas, LUA_REGISTRYINDEX); + lua_rawgeti(Luas, -1,slot); + if (lua_isfunction(Luas,-1)) { + int base = lua_gettop(Luas); /* function index */ + lua_pushinteger(Luas, slot); + lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ + lua_insert(Luas, base); /* put it under chunk */ + i = lua_pcall(Luas, 1, 0, base); + lua_remove(Luas, base); /* remove traceback function */ + if (i != 0) { + lua_gc(Luas, LUA_GCCOLLECT, 0); + Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + } + } + lua_settop(Luas,stacktop); + lua_active--; +} + +@ @c +static const luaL_Reg lualibs[] = { + /* standard lua libraries */ + { "_G", luaopen_base }, + { "package", luaopen_package }, + { "table", luaopen_table }, + { "io", luaopen_io }, + { "os", luaopen_os }, + { "string", luaopen_string }, + { "math", luaopen_math }, + { "debug", luaopen_debug }, + { "lpeg", luaopen_lpeg }, + { "bit32", luaopen_bit32 }, +#ifdef LuajitTeX + /* bit is only in luajit, */ + /* coroutine is loaded in a special way */ + { "bit", luaopen_bit }, +#else + { "coroutine", luaopen_coroutine }, +#endif + /* additional (public) libraries */ + { "unicode", luaopen_unicode }, + { "zip", luaopen_zip }, + { "md5", luaopen_md5 }, + { "lfs", luaopen_lfs }, + /* extra standard lua libraries */ +#ifdef LuajitTeX + { "jit", luaopen_jit }, +#endif + { "ffi", luaopen_ffi }, + /* obsolete, undocumented and for oru own testing only */ + /*{ "profiler", luaopen_profiler }, */ + /* more libraries will be loaded later */ + { NULL, NULL } +}; + +@ @c +static void do_openlibs(lua_State * L) +{ + const luaL_Reg *lib = lualibs; + for (; lib->func; lib++) { + Luas_open(lib->name,lib->func); + } +} + +@ @c +#ifdef LuajitTeX + /* in luajit load_aux is not used.*/ +#else +static int load_aux (lua_State *L, int status) { + if (status == 0) /* OK? */ + return 1; + else { + lua_pushnil(L); + lua_insert(L, -2); /* put before error message */ + return 2; /* return nil plus error message */ + } +} +#endif + +@ @c +static int luatex_loadfile (lua_State *L) { + int status = 0; + const char *fname = luaL_optstring(L, 1, NULL); + const char *mode = luaL_optstring(L, 2, NULL); +#ifdef LuajitTeX + /* 5.1 */ +#else + int env = !lua_isnone(L, 3); /* 'env' parameter? */ +#endif + if (!lua_only && !fname && interaction == batch_mode) { + lua_pushnil(L); + lua_pushstring(L, "reading from stdin is disabled in batch mode"); + return 2; /* return nil plus error message */ + } + status = luaL_loadfilex(L, fname, mode); + if (status == LUA_OK) { + recorder_record_input(fname); +#ifdef LuajitTeX + /* 5.1 */ +#else + if (env) { /* 'env' parameter? */ + lua_pushvalue(L, 3); + lua_setupvalue(L, -2, 1); /* set it as 1st upvalue of loaded chunk */ + } +#endif + } +#ifdef LuajitTeX + return RESERVED_load_aux_JIT(L, status,3); +#else + return load_aux(L, status); +#endif +} + +@ @c +static int luatex_dofile (lua_State *L) { + const char *fname = luaL_optstring(L, 1, NULL); + int n = lua_gettop(L); + if (!lua_only && !fname) { + if (interaction == batch_mode) { + lua_pushnil(L); + lua_pushstring(L, "reading from stdin is disabled in batch mode"); + return 2; /* return nil plus error message */ + } else { + tprint_nl("lua> "); + } + } + if (luaL_loadfile(L, fname) != 0) + lua_error(L); + recorder_record_input(fname); + lua_call(L, 0, LUA_MULTRET); + return lua_gettop(L) - n; +} + +@ @c +void luainterpreter(void) +{ + lua_State *L; +#ifdef LuajitTeX + if (jithash_hashname == NULL) { + /* default lua51 */ + luajittex_choose_hash_function = 0; + jithash_hashname = (char *) xmalloc(strlen("lua51") + 1); + jithash_hashname = strcpy ( jithash_hashname, "lua51"); + } else if (strcmp((const char*)jithash_hashname,"lua51") == 0) { + luajittex_choose_hash_function = 0; + } else if (strcmp((const char*)jithash_hashname,"luajit20") == 0) { + luajittex_choose_hash_function = 1; + } else { + /* default lua51 */ + luajittex_choose_hash_function = 0; + jithash_hashname = strcpy ( jithash_hashname, "lua51"); + } + L = luaL_newstate() ; +#else + L = lua_newstate(my_luaalloc, NULL); +#endif + if (L == NULL) { + fprintf(stderr, "Can't create the Lua state.\n"); + return; + } + lua_atpanic(L, &my_luapanic); + + do_openlibs(L); /* does all the 'simple' libraries */ +#ifdef LuajitTeX + if (luajiton){ + luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|LUAJIT_MODE_ON); + } + else { + luaJIT_setmode(L, 0, LUAJIT_MODE_ENGINE|LUAJIT_MODE_OFF); + } +#endif + + lua_pushcfunction(L,luatex_dofile); + lua_setglobal(L, "dofile"); + lua_pushcfunction(L,luatex_loadfile); + lua_setglobal(L, "loadfile"); + + open_oslibext(L); + open_strlibext(L); + open_lfslibext(L); + + /* + The socket and mime libraries are a bit tricky to open because they use a load-time + dependency that has to be worked around for luatex, where the C module is loaded + way before the lua module. + */ + + if (!nosocket_option) { + /* todo: move this to common */ + lua_getglobal(L, "package"); + lua_getfield(L, -1, "loaded"); + if (!lua_istable(L, -1)) { + lua_newtable(L); + lua_setfield(L, -2, "loaded"); + lua_getfield(L, -1, "loaded"); + } + luaopen_socket_core(L); + lua_setfield(L, -2, "socket.core"); + lua_pushnil(L); + lua_setfield(L, -2, "socket"); /* package.loaded.socket = nil */ + + luaopen_mime_core(L); + lua_setfield(L, -2, "mime.core"); + lua_pushnil(L); + lua_setfield(L, -2, "mime"); /* package.loaded.mime = nil */ + lua_pop(L, 2); /* pop the tables */ + + luatex_socketlua_open(L); /* preload the pure lua modules */ + } + + /* zlib. slightly odd calling convention */ + luaopen_zlib(L); + lua_setglobal(L, "zlib"); + + luaopen_gzip(L); + + /* our own libraries register themselves */ + + luaopen_fio(L); + luaopen_ff(L); + luaopen_tex(L); + luaopen_token(L); + luaopen_node(L); + luaopen_texio(L); + luaopen_kpse(L); + luaopen_callback(L); + + luaopen_lua(L, startup_filename); + + luaopen_stats(L); + luaopen_font(L); + luaopen_lang(L); + luaopen_mplib(L); + luaopen_vf(L); + luaopen_pdf(L); + luaopen_epdf(L); + luaopen_pdfscanner(L); + + if (!lua_only) { + luaopen_img(L); + } + + lua_createtable(L, 0, 0); + lua_setglobal(L, "texconfig"); + + Luas = L; +} + +@ @c +int hide_lua_table(lua_State * L, const char *name) +{ + int r = 0; + lua_getglobal(L, name); + if (lua_istable(L, -1)) { + r = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushnil(L); + lua_setglobal(L, name); + } + return r; +} + +@ @c +void unhide_lua_table(lua_State * L, const char *name, int r) +{ + lua_rawgeti(L, LUA_REGISTRYINDEX, r); + lua_setglobal(L, name); + luaL_unref(L, LUA_REGISTRYINDEX, r); +} + +@ @c +int hide_lua_value(lua_State * L, const char *name, const char *item) +{ + int r = 0; + lua_getglobal(L, name); + if (lua_istable(L, -1)) { + lua_getfield(L, -1, item); + r = luaL_ref(L, LUA_REGISTRYINDEX); + lua_pushnil(L); + lua_setfield(L, -2, item); + } + return r; +} + +@ @c +void unhide_lua_value(lua_State * L, const char *name, const char *item, int r) +{ + lua_getglobal(L, name); + if (lua_istable(L, -1)) { + lua_rawgeti(L, LUA_REGISTRYINDEX, r); + lua_setfield(L, -2, item); + luaL_unref(L, LUA_REGISTRYINDEX, r); + } +} + +@ @c +int lua_traceback(lua_State * L) +{ + lua_getglobal(L, "debug"); + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return 1; + } + lua_getfield(L, -1, "traceback"); + if (!lua_isfunction(L, -1)) { + lua_pop(L, 2); + return 1; + } + lua_pushvalue(L, 1); /* pass error message */ + lua_pushinteger(L, 2); /* skip this function and traceback */ + lua_call(L, 2, 1); /* call debug.traceback */ + return 1; +} + +@ @c +static void luacall(int p, int nameptr, boolean is_string) /* hh-ls: optimized lua_id resolving */ +{ + LoadS ls; + int i; + size_t ll = 0; + char *lua_id; + char *s = NULL; + + if (Luas == NULL) { + luainterpreter(); + } + lua_active++; + if (is_string) { + const char *ss = NULL; + lua_rawgeti(Luas, LUA_REGISTRYINDEX, p); + if (lua_isfunction(Luas,-1)) { + int base = lua_gettop(Luas); /* function index */ + lua_checkstack(Luas, 1); + lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ + lua_insert(Luas, base); /* put it under chunk */ + i = lua_pcall(Luas, 0, 0, base); + lua_remove(Luas, base); /* remove traceback function */ + if (i != 0) { + lua_gc(Luas, LUA_GCCOLLECT, 0); + Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + } + lua_active--; + return ; + } + ss = lua_tolstring(Luas, -1, &ll); + s = xmalloc(ll+1); + memcpy(s,ss,ll+1); + lua_pop(Luas,1); + } else { + int l = 0; + s = tokenlist_to_cstring(p, 1, &l); + ll = (size_t)l; + } + ls.s = s; + ls.size = ll; + if (ls.size > 0) { + if (nameptr > 0) { + int l = 0; /* not used */ + lua_id = tokenlist_to_cstring(nameptr, 1, &l); + i = Luas_load(Luas, getS, &ls, lua_id); + xfree(lua_id); + } else if (nameptr < 0) { + lua_id = get_lua_name((nameptr + 65536)); + if (lua_id != NULL) { + i = Luas_load(Luas, getS, &ls, lua_id); + } else { + i = Luas_load(Luas, getS, &ls, "=[\\latelua]"); + } + } else { + i = Luas_load(Luas, getS, &ls, "=[\\latelua]"); + } + if (i != 0) { + Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); + } else { + int base = lua_gettop(Luas); /* function index */ + lua_checkstack(Luas, 1); + lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ + lua_insert(Luas, base); /* put it under chunk */ + i = lua_pcall(Luas, 0, 0, base); + lua_remove(Luas, base); /* remove traceback function */ + if (i != 0) { + lua_gc(Luas, LUA_GCCOLLECT, 0); + Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + } + } + xfree(ls.s); + } + lua_active--; +} + +@ @c +void late_lua(PDF pdf, halfword p) +{ + (void) pdf; + if (late_lua_type(p)==normal) { + expand_macros_in_tokenlist(p); /* sets |def_ref| */ + luacall(def_ref, late_lua_name(p), false); + flush_list(def_ref); + } else { + luacall(late_lua_data(p), late_lua_name(p), true); + } +} + +@ @c +void luatokencall(int p, int nameptr) /* hh-ls: optimized lua_id resolving */ +{ + LoadS ls; + int i, l; + char *s = NULL; + char *lua_id; + assert(Luas); + l = 0; + lua_active++; + s = tokenlist_to_cstring(p, 1, &l); + ls.s = s; + ls.size = (size_t) l; + if (ls.size > 0) { + if (nameptr > 0) { + lua_id = tokenlist_to_cstring(nameptr, 1, &l); + i = Luas_load(Luas, getS, &ls, lua_id); + xfree(lua_id); + } else if (nameptr < 0) { + lua_id = get_lua_name((nameptr + 65536)); + if (lua_id != NULL) { + i = Luas_load(Luas, getS, &ls, lua_id); + } else { + i = Luas_load(Luas, getS, &ls, "=[\\directlua]"); + } + } else { + i = Luas_load(Luas, getS, &ls, "=[\\directlua]"); + } + xfree(s); + if (i != 0) { + Luas = luatex_error(Luas, (i == LUA_ERRSYNTAX ? 0 : 1)); + } else { + int base = lua_gettop(Luas); /* function index */ + lua_checkstack(Luas, 1); + lua_pushcfunction(Luas, lua_traceback); /* push traceback function */ + lua_insert(Luas, base); /* put it under chunk */ + i = lua_pcall(Luas, 0, 0, base); + lua_remove(Luas, base); /* remove traceback function */ + if (i != 0) { + lua_gc(Luas, LUA_GCCOLLECT, 0); + Luas = luatex_error(Luas, (i == LUA_ERRRUN ? 0 : 1)); + } + } + } + lua_active--; +} + +@ @c +lua_State *luatex_error(lua_State * L, int is_fatal) +{ + + const_lstring luaerr; + char *err = NULL; + if (lua_type(L, -1) == LUA_TSTRING) { + luaerr.s = lua_tolstring(L, -1, &luaerr.l); + /* free last one ? */ + err = (char *) xmalloc((unsigned) (luaerr.l + 1)); + snprintf(err, (luaerr.l + 1), "%s", luaerr.s); + last_lua_error = err; /* hm, what if we have several .. not freed */ + } + if (is_fatal > 0) { + /* Normally a memory error from lua. + The pool may overflow during the |maketexlstring()|, but we + are crashing anyway so we may as well abort on the pool size */ + normal_error("lua",err); + /* never reached */ + lua_close(L); + return (lua_State *) NULL; + } else { + normal_warning("lua",err); + return L; + } +} + +@ @c +void preset_environment(lua_State * L, const parm_struct * p, const char *s) +{ + int i; + assert(L != NULL); + /* double call with same s gives assert(0) */ + lua_pushstring(L, s); /* s */ + lua_gettable(L, LUA_REGISTRYINDEX); /* t */ + assert(lua_isnil(L, -1)); + lua_pop(L, 1); /* - */ + lua_pushstring(L, s); /* s */ + lua_newtable(L); /* t s */ + for (i = 1, ++p; p->name != NULL; i++, p++) { + assert(i == p->idx); + lua_pushstring(L, p->name); /* k t s */ + lua_pushinteger(L, p->idx); /* v k t s */ + lua_settable(L, -3); /* t s */ + } + lua_settable(L, LUA_REGISTRYINDEX); /* - */ +} + + +@ @c +/* + luajit compatibility layer for luatex lua5.2 +*/ +#ifdef LuajitTeX +LUALIB_API void *luaL_testudata (lua_State *L, int ud, const char *tname) { + void *p = lua_touserdata(L, ud); + if (p != NULL) { /* value is a userdata? */ + if (lua_getmetatable(L, ud)) { /* does it have a metatable? */ + luaL_getmetatable(L, tname); /* get correct metatable */ + if (!lua_rawequal(L, -1, -2)) /* not the same? */ + p = NULL; /* value is a userdata with wrong metatable */ + lua_pop(L, 2); /* remove both metatables */ + return p; + } + } + return NULL; /* value is not a userdata with a metatable */ +} + +@ @c +/* It's not ok. See lua-users.org/wiki/CompatibilityWithLuaFive for another solution */ +LUALIB_API void luaL_setfuncs (lua_State *L, const luaL_Reg *l, int nup) { + /*luaL_checkversion(L);*/ + luaL_checkstack(L, nup, "too many upvalues"); + for (; l->name != NULL; l++) { /* fill the table with given functions */ + int i; + for (i = 0; i < nup; i++) /* copy upvalues to the top */ + lua_pushvalue(L, -nup); + lua_pushcclosure(L, l->func, nup); /* closure with those upvalues */ + lua_setfield(L, -(nup + 2), l->name); + } + lua_pop(L, nup); /* remove upvalues */ +} + +@ @c +LUALIB_API char *luaL_prepbuffsize (luaL_Buffer *B, size_t sz) { + lua_State *L = B->L; + if (sz > LUAL_BUFFERSIZE ) + luaL_error(L, "buffer too large"); + return luaL_prepbuffer(B) ; +} + +@ @c +LUA_API int lua_compare (lua_State *L, int o1, int o2, int op) { + /*StkId o1, o2;*/ + int i = 0; + lua_lock(L); /* may call tag method */ + /* o1 = index2addr(L, index1); */ + /* o2 = index2addr(L, index2); */ + /*if (isvalid(o1) && isvalid(o2)) {*/ + switch (op) { + case LUA_OPEQ: i = lua_equal(L, o1, o2); break; + case LUA_OPLT: i = lua_lessthan(L, o1, o2); break; + case LUA_OPLE: i = (lua_lessthan(L, o1, o2) || lua_equal(L, o1, o2)) ; break; + default: luaL_error(L, "invalid option"); + } + /*}*/ + lua_unlock(L); + return i; +} + +@ @c +#endif diff --git a/Build/source/texk/web2c/luatexdir/lua/luatex-api.h b/Build/source/texk/web2c/luatexdir/lua/luatex-api.h index f7c612ce204..7d34607b9e0 100644 --- a/Build/source/texk/web2c/luatexdir/lua/luatex-api.h +++ b/Build/source/texk/web2c/luatexdir/lua/luatex-api.h @@ -105,9 +105,9 @@ extern int l_new_image(lua_State * L); extern int luaopen_epdf(lua_State * L); extern int luaopen_pdfscanner(lua_State * L); extern int luaopen_mplib(lua_State * L); +extern int luaopen_fio(lua_State * L); -extern void open_oslibext(lua_State * L, int safer_option); -extern int open_iolibext(lua_State * L); +extern void open_oslibext(lua_State * L); extern void open_strlibext(lua_State * L); extern void open_lfslibext(lua_State * L); @@ -154,6 +154,12 @@ extern void dump_luac_registers(void); extern void undump_luac_registers(void); extern int lua_only; +extern const char *lc_ctype; +extern const char *lc_collate; +extern const char *lc_numeric; + + + #ifdef LuajitTeX extern int luajiton; extern char *jithash_hashname ; @@ -1246,10 +1252,10 @@ init_lua_key_alias(term_and_log,"term and log") } \ } while(0) -#ifdef __MINGW32__ -extern FILE *_cairo_win32_tmpfile( void ); -#define tmpfile() _cairo_win32_tmpfile() -#endif /* __MINGW32__ */ +#ifdef _WIN32 +extern FILE *_cairo_win_tmpfile( void ); +#define tmpfile() _cairo_win_tmpfile() +#endif /* _WIN32 */ #endif /* LUATEX_API_H */ diff --git a/Build/source/texk/web2c/luatexdir/lua/luatex-core.c b/Build/source/texk/web2c/luatexdir/lua/luatex-core.c new file mode 100644 index 00000000000..c5426b2c90e --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/lua/luatex-core.c @@ -0,0 +1,253 @@ +/* generated from and by luatex-core.lua */ + +#include "lua.h" +#include "lauxlib.h" + +int load_luatex_core_lua (lua_State * L); + +int load_luatex_core_lua (lua_State * L) +{ + static unsigned char luatex_core_lua[] = { + 0x2d, 0x2d, 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, + 0x73, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, 0x20, 0x3d, + 0x20, 0x7b, 0x20, 0x7d, 0x20, 0x65, 0x6e, 0x64, 0x20, 0x6d, 0x6f, 0x64, 0x75, 0x6c, 0x65, 0x73, + 0x20, 0x5b, 0x27, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x2d, 0x63, 0x6f, 0x72, 0x65, 0x27, 0x5d, + 0x20, 0x3d, 0x20, 0x7b, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x76, 0x65, 0x72, 0x73, + 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x30, 0x31, 0x2c, 0x0a, 0x2d, + 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x27, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x6e, 0x69, 0x6f, 0x6e, 0x20, 0x74, 0x6f, 0x20, + 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x27, 0x2c, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x27, 0x48, 0x61, 0x6e, + 0x73, 0x20, 0x48, 0x61, 0x67, 0x65, 0x6e, 0x20, 0x26, 0x20, 0x4c, 0x75, 0x69, 0x67, 0x69, 0x20, + 0x53, 0x63, 0x61, 0x72, 0x73, 0x6f, 0x27, 0x2c, 0x0a, 0x2d, 0x2d, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x63, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x3d, 0x20, 0x27, 0x4c, 0x75, 0x61, + 0x54, 0x65, 0x58, 0x20, 0x44, 0x65, 0x76, 0x65, 0x6c, 0x6f, 0x70, 0x6d, 0x65, 0x6e, 0x74, 0x20, + 0x54, 0x65, 0x61, 0x6d, 0x27, 0x2c, 0x0a, 0x2d, 0x2d, 0x20, 0x7d, 0x0a, 0x0a, 0x4c, 0x55, 0x41, + 0x54, 0x45, 0x58, 0x43, 0x4f, 0x52, 0x45, 0x56, 0x45, 0x52, 0x53, 0x49, 0x4f, 0x4e, 0x20, 0x3d, + 0x20, 0x31, 0x2e, 0x30, 0x30, 0x32, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, + 0x66, 0x69, 0x6c, 0x65, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x6c, 0x6f, 0x61, 0x64, 0x73, 0x20, 0x73, + 0x6f, 0x6d, 0x65, 0x20, 0x4c, 0x75, 0x61, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x73, 0x2e, 0x20, 0x54, 0x68, 0x65, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x20, + 0x76, 0x61, 0x72, 0x69, 0x61, 0x6e, 0x74, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, + 0x20, 0x74, 0x68, 0x65, 0x20, 0x73, 0x61, 0x6d, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x66, 0x75, 0x6e, + 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x61, 0x6c, 0x69, 0x74, 0x79, 0x20, 0x61, 0x73, 0x20, 0x4c, 0x75, + 0x61, 0x54, 0x65, 0x58, 0x20, 0x3c, 0x3d, 0x20, 0x31, 0x2e, 0x30, 0x34, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x64, 0x6f, 0x69, 0x6e, 0x67, 0x20, 0x69, 0x74, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x77, + 0x61, 0x79, 0x20, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x74, 0x73, 0x20, 0x75, 0x73, 0x20, 0x74, 0x6f, + 0x20, 0x6b, 0x65, 0x65, 0x70, 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x6f, 0x72, 0x69, + 0x67, 0x69, 0x6e, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x20, 0x6c, 0x69, 0x62, 0x72, 0x61, 0x72, 0x69, + 0x65, 0x73, 0x20, 0x63, 0x6c, 0x65, 0x61, 0x6e, 0x2e, 0x20, 0x50, 0x65, 0x72, 0x66, 0x6f, 0x72, + 0x6d, 0x61, 0x6e, 0x63, 0x65, 0x20, 0x69, 0x73, 0x20, 0x70, 0x72, 0x6f, 0x62, 0x61, 0x62, 0x6c, + 0x79, 0x20, 0x65, 0x76, 0x65, 0x6e, 0x20, 0x61, 0x20, 0x62, 0x69, 0x74, 0x20, 0x62, 0x65, 0x74, + 0x74, 0x65, 0x72, 0x20, 0x6e, 0x6f, 0x77, 0x2e, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, + 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x6e, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x67, 0x65, 0x74, 0x6d, + 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, + 0x65, 0x20, 0x3d, 0x20, 0x74, 0x79, 0x70, 0x65, 0x2c, 0x20, 0x6e, 0x65, 0x78, 0x74, 0x2c, 0x20, + 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x2c, 0x20, 0x72, 0x65, + 0x71, 0x75, 0x69, 0x72, 0x65, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x6e, 0x64, + 0x2c, 0x20, 0x67, 0x73, 0x75, 0x62, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, + 0x66, 0x69, 0x6e, 0x64, 0x2c, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, 0x67, 0x73, 0x75, + 0x62, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, + 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x69, 0x6f, 0x5f, + 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x20, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, + 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x69, + 0x6f, 0x2e, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, + 0x73, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, + 0x61, 0x6d, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x2e, 0x72, 0x65, 0x63, 0x6f, 0x72, + 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x20, 0x6d, 0x74, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x67, 0x65, 0x74, 0x6d, 0x65, 0x74, 0x61, 0x74, 0x61, + 0x62, 0x6c, 0x65, 0x28, 0x69, 0x6f, 0x2e, 0x73, 0x74, 0x64, 0x65, 0x72, 0x72, 0x29, 0x0a, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6d, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x2e, 0x6c, 0x69, + 0x6e, 0x65, 0x73, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, 0x6f, + 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x73, 0x61, 0x66, 0x65, 0x72, 0x5f, 0x6f, 0x70, 0x74, + 0x69, 0x6f, 0x6e, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, + 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x5f, 0x65, 0x73, 0x63, + 0x61, 0x70, 0x65, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x30, 0x20, 0x28, 0x64, 0x69, + 0x73, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x29, 0x20, 0x31, 0x20, 0x28, 0x72, 0x65, 0x73, 0x74, 0x72, + 0x69, 0x63, 0x74, 0x65, 0x64, 0x29, 0x20, 0x32, 0x20, 0x28, 0x65, 0x76, 0x65, 0x72, 0x79, 0x74, + 0x68, 0x69, 0x6e, 0x67, 0x29, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6b, 0x70, 0x73, 0x65, + 0x75, 0x73, 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x3d, 0x20, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x2e, 0x6b, 0x70, 0x73, 0x65, 0x5f, 0x75, 0x73, + 0x65, 0x64, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x30, 0x20, 0x31, + 0x0a, 0x0a, 0x69, 0x6f, 0x2e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, + 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x2d, 0x2d, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, + 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, 0x74, 0x65, 0x64, 0x0a, 0x69, 0x6f, 0x2e, 0x73, 0x61, + 0x76, 0x65, 0x64, 0x5f, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, + 0x2d, 0x2d, 0x20, 0x63, 0x61, 0x6e, 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x74, 0x65, 0x63, + 0x74, 0x65, 0x64, 0x0a, 0x69, 0x6f, 0x2e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, + 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, + 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x2d, 0x2d, 0x20, 0x61, 0x6c, 0x77, 0x61, + 0x79, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x0a, 0x6d, 0x74, 0x2e, 0x73, + 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, 0x74, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, + 0x20, 0x2d, 0x2d, 0x20, 0x61, 0x6c, 0x77, 0x61, 0x79, 0x73, 0x20, 0x72, 0x65, 0x61, 0x64, 0x6f, + 0x6e, 0x6c, 0x79, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, + 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x27, + 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, + 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, + 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x66, 0x20, 0x74, 0x79, 0x70, 0x65, 0x28, 0x68, 0x6f, 0x77, 0x29, 0x20, 0x3d, 0x3d, + 0x20, 0x27, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x27, 0x20, 0x61, 0x6e, 0x64, 0x20, 0x66, 0x69, + 0x6e, 0x64, 0x28, 0x68, 0x6f, 0x77, 0x2c, 0x27, 0x77, 0x27, 0x29, 0x20, 0x74, 0x68, 0x65, 0x6e, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x69, 0x6f, + 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, + 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x77, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x65, 0x6c, 0x73, 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, + 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, + 0x70, 0x65, 0x6e, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x28, 0x6e, 0x61, 0x6d, + 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x68, 0x6f, + 0x77, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, + 0x6f, 0x77, 0x20, 0x3d, 0x20, 0x27, 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6c, 0x73, + 0x65, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x20, + 0x67, 0x73, 0x75, 0x62, 0x28, 0x68, 0x6f, 0x77, 0x2c, 0x27, 0x5b, 0x5e, 0x72, 0x62, 0x5d, 0x27, + 0x2c, 0x27, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x68, 0x6f, 0x77, 0x20, 0x3d, 0x3d, 0x20, 0x27, 0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x68, 0x6f, 0x77, 0x20, 0x3d, + 0x20, 0x27, 0x72, 0x27, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, + 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, + 0x61, 0x6d, 0x65, 0x2c, 0x68, 0x6f, 0x77, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, + 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, + 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x63, 0x6f, 0x72, 0x64, 0x66, 0x69, 0x6c, 0x65, 0x6e, 0x61, 0x6d, + 0x65, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, + 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x70, + 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x2c, 0x20, 0x66, + 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x3d, 0x20, 0x66, 0x69, 0x6f, 0x5f, 0x63, 0x68, 0x65, 0x63, 0x6b, + 0x70, 0x65, 0x72, 0x6d, 0x69, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6f, 0x6b, 0x61, 0x79, 0x20, 0x61, 0x6e, 0x64, + 0x20, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x69, 0x6f, 0x5f, 0x70, 0x6f, + 0x70, 0x65, 0x6e, 0x28, 0x66, 0x6f, 0x75, 0x6e, 0x64, 0x2c, 0x2e, 0x2e, 0x2e, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, + 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x20, 0x3d, 0x20, 0x69, + 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x28, 0x6e, 0x61, 0x6d, 0x65, 0x2c, 0x27, 0x72, 0x27, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x66, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x69, 0x6f, 0x5f, + 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, + 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x72, 0x65, + 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, + 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x29, 0x0a, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, 0x6c, 0x69, 0x6e, 0x65, 0x28, 0x66, 0x29, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x6f, 0x2e, 0x6c, + 0x69, 0x6e, 0x65, 0x73, 0x20, 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, + 0x5f, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x0a, 0x6d, 0x74, 0x2e, 0x6c, 0x69, 0x6e, 0x65, 0x73, 0x20, + 0x3d, 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x72, 0x65, 0x61, 0x64, + 0x6c, 0x69, 0x6e, 0x65, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, 0x57, 0x65, 0x20, 0x61, 0x73, 0x73, 0x75, + 0x6d, 0x65, 0x20, 0x6d, 0x61, 0x6e, 0x61, 0x67, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, 0x74, 0x6f, + 0x20, 0x62, 0x65, 0x20, 0x70, 0x72, 0x6f, 0x76, 0x69, 0x64, 0x65, 0x64, 0x20, 0x62, 0x79, 0x20, + 0x74, 0x68, 0x65, 0x20, 0x72, 0x65, 0x70, 0x6c, 0x61, 0x63, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x20, + 0x6f, 0x66, 0x20, 0x6b, 0x70, 0x73, 0x65, 0x2e, 0x20, 0x54, 0x68, 0x69, 0x73, 0x20, 0x69, 0x73, + 0x20, 0x74, 0x68, 0x65, 0x0a, 0x2d, 0x2d, 0x20, 0x63, 0x61, 0x73, 0x65, 0x20, 0x69, 0x6e, 0x20, + 0x43, 0x6f, 0x6e, 0x54, 0x65, 0x58, 0x74, 0x2e, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x6b, 0x70, 0x73, + 0x65, 0x75, 0x73, 0x65, 0x64, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x3d, 0x20, + 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x20, + 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x6c, 0x75, + 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x0a, 0x0a, 0x65, + 0x6e, 0x64, 0x0a, 0x0a, 0x69, 0x66, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, 0x6f, 0x70, 0x74, 0x69, + 0x6f, 0x6e, 0x20, 0x3d, 0x3d, 0x20, 0x31, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x20, 0x3d, 0x20, 0x6e, + 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x73, 0x70, 0x61, 0x77, 0x6e, 0x20, + 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x65, + 0x78, 0x65, 0x63, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6f, 0x73, 0x2e, 0x73, 0x65, 0x74, 0x65, 0x6e, 0x76, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, + 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x74, 0x65, 0x6d, 0x70, 0x64, 0x69, 0x72, + 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x70, + 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, + 0x6c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, + 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6f, 0x73, 0x2e, 0x72, + 0x65, 0x6d, 0x6f, 0x76, 0x65, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x74, 0x6d, 0x70, 0x66, 0x69, 0x6c, 0x65, 0x20, 0x3d, 0x20, 0x6e, + 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x6f, 0x75, 0x74, 0x70, 0x75, 0x74, + 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, + 0x2e, 0x63, 0x68, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x6c, 0x6f, 0x63, 0x6b, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6e, + 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x74, 0x6f, 0x75, 0x63, 0x68, + 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x66, 0x73, 0x2e, + 0x72, 0x6d, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x6c, 0x66, 0x73, 0x2e, 0x6d, 0x6b, 0x64, 0x69, 0x72, 0x20, 0x20, 0x3d, 0x20, 0x6e, 0x69, + 0x6c, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x69, 0x6f, 0x2e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x5f, + 0x70, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x69, 0x6f, 0x2e, 0x73, 0x61, 0x76, 0x65, 0x64, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x20, 0x20, 0x3d, + 0x20, 0x6c, 0x75, 0x61, 0x74, 0x65, 0x78, 0x5f, 0x69, 0x6f, 0x5f, 0x6f, 0x70, 0x65, 0x6e, 0x5f, + 0x72, 0x65, 0x61, 0x64, 0x6f, 0x6e, 0x6c, 0x79, 0x0a, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x69, + 0x66, 0x20, 0x73, 0x61, 0x66, 0x65, 0x72, 0x6f, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x3d, 0x3d, + 0x20, 0x31, 0x20, 0x6f, 0x72, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, 0x73, 0x63, 0x61, 0x70, + 0x65, 0x20, 0x7e, 0x3d, 0x20, 0x32, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x66, 0x66, 0x69, 0x20, 0x3d, 0x20, 0x72, 0x65, 0x71, 0x75, 0x69, 0x72, 0x65, 0x28, 0x27, + 0x66, 0x66, 0x69, 0x27, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x6f, 0x72, 0x20, 0x6b, 0x2c, + 0x20, 0x76, 0x20, 0x69, 0x6e, 0x20, 0x6e, 0x65, 0x78, 0x74, 0x2c, 0x20, 0x66, 0x66, 0x69, 0x20, + 0x64, 0x6f, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x69, 0x66, 0x20, 0x6b, 0x20, + 0x7e, 0x3d, 0x20, 0x27, 0x67, 0x63, 0x27, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, 0x69, 0x5b, 0x6b, 0x5d, 0x20, + 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, + 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x66, + 0x69, 0x20, 0x3d, 0x20, 0x6e, 0x69, 0x6c, 0x0a, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x2d, 0x2d, 0x20, + 0x6f, 0x73, 0x2e, 0x5b, 0x65, 0x78, 0x65, 0x63, 0x75, 0x74, 0x65, 0x7c, 0x6f, 0x73, 0x2e, 0x73, + 0x70, 0x61, 0x77, 0x6e, 0x7c, 0x6f, 0x73, 0x2e, 0x65, 0x78, 0x65, 0x63, 0x5d, 0x20, 0x61, 0x6c, + 0x72, 0x65, 0x61, 0x64, 0x79, 0x20, 0x61, 0x72, 0x65, 0x20, 0x73, 0x68, 0x65, 0x6c, 0x6c, 0x65, + 0x73, 0x63, 0x61, 0x70, 0x65, 0x20, 0x61, 0x77, 0x61, 0x72, 0x65, 0x29, 0x0a, 0x0a, 0x0a, 0x69, + 0x66, 0x20, 0x6d, 0x64, 0x35, 0x20, 0x74, 0x68, 0x65, 0x6e, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, 0x73, 0x75, 0x6d, 0x20, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x6d, + 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x20, 0x67, 0x73, 0x75, 0x62, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x2e, 0x67, 0x73, 0x75, 0x62, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x20, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 0x2e, + 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x6c, 0x6f, 0x63, 0x61, 0x6c, + 0x20, 0x62, 0x79, 0x74, 0x65, 0x20, 0x20, 0x20, 0x3d, 0x20, 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, + 0x2e, 0x62, 0x79, 0x74, 0x65, 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x20, 0x6d, 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x68, 0x65, 0x78, 0x61, 0x28, + 0x6b, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, + 0x6e, 0x20, 0x28, 0x67, 0x73, 0x75, 0x62, 0x28, 0x73, 0x75, 0x6d, 0x28, 0x6b, 0x29, 0x2c, 0x20, + 0x22, 0x2e, 0x22, 0x2c, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x29, + 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, + 0x75, 0x72, 0x6e, 0x20, 0x66, 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x22, 0x25, 0x30, 0x32, 0x78, + 0x22, 0x2c, 0x62, 0x79, 0x74, 0x65, 0x28, 0x63, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, + 0x0a, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x20, 0x6d, + 0x64, 0x35, 0x2e, 0x73, 0x75, 0x6d, 0x48, 0x45, 0x58, 0x41, 0x28, 0x6b, 0x29, 0x0a, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x28, 0x67, 0x73, + 0x75, 0x62, 0x28, 0x73, 0x75, 0x6d, 0x28, 0x6b, 0x29, 0x2c, 0x20, 0x22, 0x2e, 0x22, 0x2c, 0x20, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x28, 0x63, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x72, 0x65, 0x74, 0x75, 0x72, 0x6e, 0x20, 0x66, + 0x6f, 0x72, 0x6d, 0x61, 0x74, 0x28, 0x22, 0x25, 0x30, 0x32, 0x58, 0x22, 0x2c, 0x62, 0x79, 0x74, + 0x65, 0x28, 0x63, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, + 0x64, 0x29, 0x29, 0x0a, 0x20, 0x20, 0x20, 0x20, 0x65, 0x6e, 0x64, 0x0a, 0x0a, 0x65, 0x6e, 0x64, + 0x0a, 0x0a, 0x00 + }; + return luaL_dostring(L, (const char*) luatex_core_lua); +} \ No newline at end of file diff --git a/Build/source/texk/web2c/luatexdir/lua/luatex-core.lua b/Build/source/texk/web2c/luatexdir/lua/luatex-core.lua new file mode 100644 index 00000000000..704e44c2400 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/lua/luatex-core.lua @@ -0,0 +1,209 @@ +-- if not modules then modules = { } end modules ['luatex-core'] = { +-- version = 1.001, +-- comment = 'companion to luatex', +-- author = 'Hans Hagen & Luigi Scarso', +-- copyright = 'LuaTeX Development Team', +-- } + +LUATEXCOREVERSION = 1.002 + +-- This file overloads some Lua functions. The readline variants provide the same +-- functionality as LuaTeX <= 1.04 and doing it this way permits us to keep the +-- original io libraries clean. Performance is probably even a bit better now. + +local type, next, getmetatable, require = type, next, getmetatable, require +local find, gsub = string.find, string.gsub + +local io_open = io.open +local io_popen = io.popen +local io_line = io.lines + +local fio_readline = fio.readline +local fio_checkpermission = fio.checkpermission +local fio_recordfilename = fio.recordfilename + +local mt = getmetatable(io.stderr) +local mt_lines = mt.lines +local saferoption = status.safer_option +local shellescape = status.shell_escape -- 0 (disabled) 1 (restricted) 2 (everything) +local kpseused = status.kpse_used -- 0 1 + +io.saved_open = io_open -- can be protected +io.saved_popen = io_popen -- can be protected +io.saved_lines = io_lines -- always readonly +mt.saved_lines = mt_lines -- always readonly + +local function luatex_io_open(name,how) + if not how then + how = 'r' + end + local f = io_open(name,how) + if f then + if type(how) == 'string' and find(how,'w') then + fio_recordfilename(name,'w') + else + fio_recordfilename(name,'r') + end + end + return f +end + +local function luatex_io_open_readonly(name,how) + if how then + how = 'r' + else + how = gsub(how,'[^rb]','') + if how == '' then + how = 'r' + end + end + local f = io_open(name,how) + if f then + fio_recordfilename(name,'r') + end + return f +end + +local function luatex_io_popen(name,...) + local okay, found = fio_checkpermission(name) + if okay and found then + return io_popen(found,...) + end +end + +local function luatex_io_lines(name) + local f = io_open(name,'r') + if f then + return function() + return fio_readline(f) + end + end +end + +local function luatex_io_readline(f) + return function() + return fio_readline(f) + end +end + +io.lines = luatex_io_lines +mt.lines = luatex_io_readline + +-- We assume management to be provided by the replacement of kpse. This is the +-- case in ConTeXt. + +if kpseused == 1 then + + io.open = luatex_io_open + io.popen = luatex_io_popen + +end + +if saferoption == 1 then + + os.execute = nil + os.spawn = nil + os.exec = nil + os.setenv = nil + os.tempdir = nil + + io.popen = nil + io.open = nil + + os.rename = nil + os.remove = nil + + io.tmpfile = nil + io.output = nil + + lfs.chdir = nil + lfs.lock = nil + lfs.touch = nil + lfs.rmdir = nil + lfs.mkdir = nil + + io.saved_popen = nil + io.saved_open = luatex_io_open_readonly + +end + +if saferoption == 1 or shellescape ~= 2 then + + ffi = require('ffi') + for k, v in next, ffi do + if k ~= 'gc' then + ffi[k] = nil + end + end + ffi = nil +end + +-- os.[execute|os.spawn|os.exec] already are shellescape aware) + + +if md5 then + + local sum = md5.sum + local gsub = string.gsub + local format = string.format + local byte = string.byte + + function md5.sumhexa(k) + return (gsub(sum(k), ".", function(c) + return format("%02x",byte(c)) + end)) + end + + function md5.sumHEXA(k) + return (gsub(sum(k), ".", function(c) + return format("%02X",byte(c)) + end)) + end + +end + +if utilities and utilities.merger and utilities.merger.compact then + + local byte, format, gmatch = string.byte, string.format, string.gmatch + local concat = table.concat + + local data = gsub(io.loaddata('luatex-core.lua'),'if%s+utilities.*','') + local t = { } + local r = { } + local n = 0 + local d = gsub(data,'\r\n','\n') -- be nice for unix + local s = utilities.merger.compact(d) -- no comments and less spaces + + t[#t+1] = '/* generated from and by luatex-core.lua */' + t[#t+1] = '' + -- t[#t+1] = format('/*\n\n%s\n\n*/',d) + -- t[#t+1] = '' + t[#t+1] = '#include "lua.h"' + t[#t+1] = '#include "lauxlib.h"' + t[#t+1] = '' + t[#t+1] = 'int load_luatex_core_lua (lua_State * L);' + t[#t+1] = '' + t[#t+1] = 'int load_luatex_core_lua (lua_State * L)' + t[#t+1] = '{' + t[#t+1] = ' static unsigned char luatex_core_lua[] = {' + for c in gmatch(d,'.') do + if n == 16 then + n = 1 + t[#t+1] = ' ' .. concat(r,', ') .. ',' + else + n = n + 1 + end + r[n] = format('0x%02x',byte(c)) + end + n = n + 1 + r[n] = '0x00' + t[#t+1] = ' ' .. concat(r,', ',1,n) + t[#t+1] = ' };' + -- t[#t+1] = format('unsigned int luatex_core_lua_len = 0x%x;',#d+1) + t[#t+1] = ' return luaL_dostring(L, (const char*) luatex_core_lua);' + t[#t+1] = '}' + + io.savedata('luatex-core.c',concat(t,'\n')) + io.savedata('luatex-core-stripped.lua',s) + +end diff --git a/Build/source/texk/web2c/luatexdir/lua/mplibstuff.w b/Build/source/texk/web2c/luatexdir/lua/mplibstuff.w new file mode 100644 index 00000000000..3104a287007 --- /dev/null +++ b/Build/source/texk/web2c/luatexdir/lua/mplibstuff.w @@ -0,0 +1,93 @@ +% mplibstuff.w +% +% Copyright 2017 LuaTeX team +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ PNG and SVG backends are not available in \LuaTeX, because it's complex +to manage the math formulas at run time. In this respect |PostScript| and the highlevel |objects| +are better, and they are the standard way. Another problem is how to emit the warning: +the |normal\_warning| function is not available when \LuaTeX\ is called as LUA only. + + + +@c +#include + +extern void normal_warning(const char *t, const char *p); +extern int lua_only; +#define mplibstuff_message(BACKEND) do { \ + if (lua_only) { \ + fprintf(stdout,"mplib: " #BACKEND " backend not available.\n"); \ + } else { \ + normal_warning("mplib", #BACKEND " backend not available."); \ + } \ +} while (0) + + +@ @c +void mp_png_backend_initialize (void *mp); +void mp_png_backend_free (void *mp); +int mp_png_gr_ship_out (void *hh, void *options, int standalone); +int mp_png_ship_out (void *hh, const char *options); + +@ @c +void mp_svg_backend_initialize (void *mp); +void mp_svg_backend_free (void *mp); +int mp_svg_ship_out (void *hh, int prologues); +int mp_svg_gr_ship_out (void *hh, int qprologues, int standalone); + +@ @c +void mp_png_backend_initialize (void *mp) {return; } /*{mplibstuff_message(1png);return;}*/ +void mp_png_backend_free (void *mp) {return; } /*{mplibstuff_message(png);return;}*/ +int mp_png_gr_ship_out (void *hh, void *options, int standalone) {mplibstuff_message(png);return 1;} +int mp_png_ship_out (void *hh, const char *options) {mplibstuff_message(png);return 1;} + +@ @c +void mp_svg_backend_initialize (void *mp) {return;} /*{mplibstuff_message(svg);return;}*/ +void mp_svg_backend_free (void *mp) {return;} /*{mplibstuff_message(svg);return;}*/ +int mp_svg_ship_out (void *hh, int prologues) {mplibstuff_message(svg);return 1;} +int mp_svg_gr_ship_out (void *hh, int qprologues, int standalone) {mplibstuff_message(svg);return 1;} + +@ @c +const char* +cairo_version_string (void); +const char* +pixman_version_string (void); +#define CAIRO_VERSION_STRING "CAIRO NOT AVAILABLE" +const char *COMPILED_CAIRO_VERSION_STRING = CAIRO_VERSION_STRING; +#define PIXMAN_VERSION_STRING "PIXMAN NOT AVAILABLE" +const char *COMPILED_PIXMAN_VERSION_STRING = PIXMAN_VERSION_STRING; + +const char* +cairo_version_string (void) +{ + return CAIRO_VERSION_STRING; +} + +const char* +pixman_version_string (void) +{ + return PIXMAN_VERSION_STRING; +} + + + + + +@ @c +char png_libpng_ver[] = "PNG NOT AVAILABLE"; + diff --git a/Build/source/texk/web2c/luatexdir/luaffi/README b/Build/source/texk/web2c/luatexdir/luaffi/README index 8093da53c41..bff350f6f64 100644 --- a/Build/source/texk/web2c/luatexdir/luaffi/README +++ b/Build/source/texk/web2c/luatexdir/luaffi/README @@ -8,7 +8,7 @@ local info = [[ The ffi module is available for: archictures : ARCH_X86 and ARCH_X64, - operating systems : OS_CE, OS_WIN, OS_LINUX, OS BD and OS_POSIX + operating systems : OS_CE, OS_WIN, OS_LINUX, OS_BSD and OS_POSIX The ARM processor is currently not supported. There are subtle differences between this module and the one in luajitTeX diff --git a/Build/source/texk/web2c/luatexdir/luaffi/ffi.c b/Build/source/texk/web2c/luatexdir/luaffi/ffi.c index d65b2bed7e1..abeeb5f1fe0 100644 --- a/Build/source/texk/web2c/luatexdir/luaffi/ffi.c +++ b/Build/source/texk/web2c/luatexdir/luaffi/ffi.c @@ -3567,7 +3567,7 @@ int luaopen_ffi(lua_State *L) "The ffi module is available for:\n" "\n" " archictures : ARCH_X86 and ARCH_X64,\n" -" operating systems : OS_CE, OS_WIN, OS_LINUX, OS BD and OS_POSIX\n" +" operating systems : OS_CE, OS_WIN, OS_LINUX, OS_BSD and OS_POSIX\n" "\n" "The ARM processor is currently not supported. There are subtle\n" "differences between this module and the one in luajitTeX \n" diff --git a/Build/source/texk/web2c/luatexdir/luaffi/ffi.h b/Build/source/texk/web2c/luatexdir/luaffi/ffi.h index 757d1a4d466..cfc25424175 100644 --- a/Build/source/texk/web2c/luatexdir/luaffi/ffi.h +++ b/Build/source/texk/web2c/luatexdir/luaffi/ffi.h @@ -151,6 +151,13 @@ static void (lua_remove)(lua_State *L, int idx) { #if (defined ARCH_X86 || defined ARCH_X64) && (defined OS_CE || defined OS_WIN || defined OS_LINUX || defined OS_BSD || defined OS_POSIX || defined OS_OSX) #define FFI_ENABLE_LUATEX_INTERFACE #endif +/* for the moment */ +#if (defined __CYGWIN__) +#undef FFI_ENABLE_LUATEX_INTERFACE +#endif + + + #ifdef _WIN32 diff --git a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/configure-pfaedit.h b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/configure-pfaedit.h index 145f8b85333..6f9380d581d 100644 --- a/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/configure-pfaedit.h +++ b/Build/source/texk/web2c/luatexdir/luafontloader/fontforge/fontforge/configure-pfaedit.h @@ -291,11 +291,11 @@ -#ifdef __MINGW32__ +#ifdef _WIN32 #include -extern FILE *_cairo_win32_tmpfile( void ); -#define tmpfile() _cairo_win32_tmpfile() -#endif /* __MINGW32__ */ +extern FILE *_cairo_win_tmpfile( void ); +#define tmpfile() _cairo_win_tmpfile() +#endif /* _WIN32 */ #endif diff --git a/Build/source/texk/web2c/luatexdir/luatex.c b/Build/source/texk/web2c/luatexdir/luatex.c index 2b924cac350..7706c6e98b3 100644 --- a/Build/source/texk/web2c/luatexdir/luatex.c +++ b/Build/source/texk/web2c/luatexdir/luatex.c @@ -29,9 +29,9 @@ #define TeX int luatex_version = 100; /* \.{\\luatexversion} */ -int luatex_revision = '3'; /* \.{\\luatexrevision} */ +int luatex_revision = '4'; /* \.{\\luatexrevision} */ int luatex_date_info = 2017021514; /* the compile date is now hardwired :YEAR MONTH DAY HOUR*/ -const char *luatex_version_string = "1.0.3"; +const char *luatex_version_string = "1.0.4"; const char *engine_name = my_name; /* the name of this engine */ #include diff --git a/Build/source/texk/web2c/luatexdir/ptexlib.h b/Build/source/texk/web2c/luatexdir/ptexlib.h index caded2b204a..21bb9348ce2 100644 --- a/Build/source/texk/web2c/luatexdir/ptexlib.h +++ b/Build/source/texk/web2c/luatexdir/ptexlib.h @@ -291,7 +291,7 @@ void vf_out_image(PDF pdf, unsigned i); /* lua/ltexiolib.c */ void flush_loggable_info(void); -/* lua/luastuff.w and lua/luajitstuff.w */ +/* lua/luastuff.w */ void luafunctioncall(int slot); /* lua/luastuff.c */ diff --git a/Build/source/texk/web2c/luatexdir/tex/commands.w b/Build/source/texk/web2c/luatexdir/tex/commands.w index c267904f0d9..84235914c64 100644 --- a/Build/source/texk/web2c/luatexdir/tex/commands.w +++ b/Build/source/texk/web2c/luatexdir/tex/commands.w @@ -1,879 +1,883 @@ -% commands.w -% -% Copyright 2009-2010 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -\def\eTeX{e-\TeX} - -@ @c - - -#include "ptexlib.h" - -@ 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. - -@c -void initialize_commands(void) -{ - - primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, glue_base); - primitive_tex("baselineskip", assign_glue_cmd, glue_base + baseline_skip_code, glue_base); - primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, glue_base); - primitive_tex("abovedisplayskip", assign_glue_cmd, glue_base + above_display_skip_code, glue_base); - primitive_tex("belowdisplayskip", assign_glue_cmd, glue_base + below_display_skip_code, glue_base); - primitive_tex("abovedisplayshortskip", assign_glue_cmd, glue_base + above_display_short_skip_code, glue_base); - primitive_tex("belowdisplayshortskip", assign_glue_cmd, glue_base + below_display_short_skip_code, glue_base); - primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, glue_base); - primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, glue_base); - primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, glue_base); - primitive_tex("splittopskip", assign_glue_cmd, glue_base + split_top_skip_code, glue_base); - primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, glue_base); - primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, glue_base); - primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, glue_base); - primitive_tex("parfillskip", assign_glue_cmd, glue_base + par_fill_skip_code, glue_base); - primitive_tex("thinmuskip", assign_mu_glue_cmd, glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code); - primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, glue_base + thin_mu_skip_code); - primitive_tex("thickmuskip", assign_mu_glue_cmd, glue_base + thick_mu_skip_code, glue_base + thin_mu_skip_code); - primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base); - primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base); - primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base); - primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base); - primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base); - primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, local_base); - primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base); - primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base); - primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base); - primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base); - primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base); - - /* The integer parameter names must be entered into the hash table */ - - primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base); - primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base); - primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, int_base); - primitive_tex("hyphenpenalty", assign_int_cmd, int_base + hyphen_penalty_code, int_base); - primitive_tex("exhyphenpenalty", assign_int_cmd, int_base + ex_hyphen_penalty_code, int_base); - primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, int_base); - primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, int_base); - primitive_tex("displaywidowpenalty", assign_int_cmd, int_base + display_widow_penalty_code, int_base); - primitive_tex("brokenpenalty", assign_int_cmd, int_base + broken_penalty_code, int_base); - primitive_tex("binoppenalty", assign_int_cmd, int_base + bin_op_penalty_code, int_base); - primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, int_base); - primitive_tex("predisplaypenalty", assign_int_cmd, int_base + pre_display_penalty_code, int_base); - primitive_tex("postdisplaypenalty", assign_int_cmd, int_base + post_display_penalty_code, int_base); - primitive_tex("interlinepenalty", assign_int_cmd, int_base + inter_line_penalty_code, int_base); - primitive_tex("doublehyphendemerits", assign_int_cmd, int_base + double_hyphen_demerits_code, int_base); - primitive_tex("finalhyphendemerits", assign_int_cmd, int_base + final_hyphen_demerits_code, int_base); - primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, int_base); - primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base); - primitive_tex("delimiterfactor", assign_int_cmd, int_base + delimiter_factor_code, int_base); - primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, int_base); - primitive_tex("time", assign_int_cmd, int_base + time_code, int_base); - primitive_tex("day", assign_int_cmd, int_base + day_code, int_base); - primitive_tex("month", assign_int_cmd, int_base + month_code, int_base); - primitive_tex("year", assign_int_cmd, int_base + year_code, int_base); - primitive_tex("showboxbreadth", assign_int_cmd, int_base + show_box_breadth_code, int_base); - primitive_tex("showboxdepth", assign_int_cmd, int_base + show_box_depth_code, int_base); - primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, int_base); - primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, int_base); - primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base); - primitive_tex("tracingonline", assign_int_cmd, int_base + tracing_online_code, int_base); - primitive_tex("tracingmacros", assign_int_cmd, int_base + tracing_macros_code, int_base); - primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, int_base); - primitive_tex("tracingparagraphs", assign_int_cmd, int_base + tracing_paragraphs_code, int_base); - primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, int_base); - primitive_tex("tracingoutput", assign_int_cmd, int_base + tracing_output_code, int_base); - primitive_tex("tracinglostchars", assign_int_cmd, int_base + tracing_lost_chars_code, int_base); - primitive_tex("tracingcommands", assign_int_cmd, int_base + tracing_commands_code, int_base); - primitive_tex("tracingrestores", assign_int_cmd, int_base + tracing_restores_code, int_base); - primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base); - primitive_tex("outputpenalty", assign_int_cmd, int_base + output_penalty_code, int_base); - primitive_tex("maxdeadcycles", assign_int_cmd, int_base + max_dead_cycles_code, int_base); - primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, int_base); - primitive_tex("floatingpenalty", assign_int_cmd, int_base + floating_penalty_code, int_base); - primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, int_base); - primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base); - primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, int_base); - primitive_tex("defaulthyphenchar", assign_int_cmd, int_base + default_hyphen_char_code, int_base); - primitive_tex("defaultskewchar", assign_int_cmd, int_base + default_skew_char_code, int_base); - primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, int_base); - primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, int_base); - primitive_tex("language", assign_int_cmd, int_base + language_code, int_base); - primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, int_base); - primitive_tex("firstvalidlanguage", assign_int_cmd, int_base + first_valid_language_code, int_base); - primitive_tex("exhyphenchar", assign_int_cmd, int_base + ex_hyphen_char_code, int_base); - primitive_tex("lefthyphenmin", assign_int_cmd, int_base + left_hyphen_min_code, int_base); - primitive_tex("righthyphenmin", assign_int_cmd, int_base + right_hyphen_min_code, int_base); - primitive_tex("holdinginserts", assign_int_cmd, int_base + holding_inserts_code, int_base); - primitive_tex("errorcontextlines", assign_int_cmd, int_base + error_context_lines_code, int_base); - primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, int_base); - primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, int_base); - primitive_luatex("nospaces", assign_int_cmd, int_base + disable_space_code, int_base); - primitive_luatex("catcodetable", assign_int_cmd, int_base + cat_code_table_code, int_base); - primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, int_base); - primitive_luatex("outputmode", assign_int_cmd, int_base + output_mode_code, int_base); - primitive_luatex("adjustspacing", assign_int_cmd, int_base + adjust_spacing_code, int_base); - primitive_luatex("protrudechars", assign_int_cmd, int_base + protrude_chars_code, int_base); - primitive_luatex("tracingfonts", assign_int_cmd, int_base + tracing_fonts_code, int_base); - primitive_luatex("draftmode", assign_int_cmd, int_base + draft_mode_code, int_base); - primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, dimen_base); - primitive_tex("mathsurround", assign_dimen_cmd, dimen_base + math_surround_code, dimen_base); - primitive_tex("lineskiplimit", assign_dimen_cmd, dimen_base + line_skip_limit_code, dimen_base); - primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, dimen_base); - primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, dimen_base); - primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, dimen_base); - primitive_tex("splitmaxdepth", assign_dimen_cmd, dimen_base + split_max_depth_code, dimen_base); - primitive_tex("boxmaxdepth", assign_dimen_cmd, dimen_base + box_max_depth_code, dimen_base); - primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, dimen_base); - primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, dimen_base); - primitive_tex("delimitershortfall", assign_dimen_cmd, dimen_base + delimiter_shortfall_code, dimen_base); - primitive_tex("nulldelimiterspace", assign_dimen_cmd, dimen_base + null_delimiter_space_code, dimen_base); - primitive_tex("scriptspace", assign_dimen_cmd, dimen_base + script_space_code, dimen_base); - primitive_tex("predisplaysize", assign_dimen_cmd, dimen_base + pre_display_size_code, dimen_base); - primitive_tex("displaywidth", assign_dimen_cmd, dimen_base + display_width_code, dimen_base); - primitive_tex("displayindent", assign_dimen_cmd, dimen_base + display_indent_code, dimen_base); - primitive_tex("overfullrule", assign_dimen_cmd, dimen_base + overfull_rule_code, dimen_base); - primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, dimen_base); - primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, dimen_base); - primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, dimen_base); - primitive_tex("emergencystretch", assign_dimen_cmd, dimen_base + emergency_stretch_code, dimen_base); - primitive_luatex("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, dimen_base); - primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base); - primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base); - primitive_luatex("predisplaygapfactor", assign_int_cmd, int_base + math_pre_display_gap_factor_code, int_base); - - /* 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_tex(" ", ex_space_cmd, 0, 0); - primitive_tex("/", ital_corr_cmd, 0, 0); - primitive_tex("accent", accent_cmd, 0, 0); - primitive_tex("advance", advance_cmd, 0, 0); - primitive_tex("afterassignment", after_assignment_cmd, 0, 0); - primitive_tex("aftergroup", after_group_cmd, 0, 0); - primitive_tex("begingroup", begin_group_cmd, 0, 0); - primitive_tex("char", char_num_cmd, 0, 0); - primitive_tex("csname", cs_name_cmd, 0, 0); - primitive_luatex("lastnamedcs", cs_name_cmd, 1, 0); - primitive_luatex("begincsname", cs_name_cmd, 2, 0); - primitive_tex("delimiter", delim_num_cmd, 0, 0); - primitive_luatex("Udelimiter", delim_num_cmd, 1, 0); - primitive_tex("divide", divide_cmd, 0, 0); - primitive_tex("endcsname", end_cs_name_cmd, 0, 0); - primitive_tex("endgroup", end_group_cmd, 0, 0); - cs_text(frozen_end_group) = maketexstring("endgroup"); - eqtb[frozen_end_group] = eqtb[cur_val]; - primitive_tex("expandafter", expand_after_cmd, 0, 0); - primitive_tex("font", def_font_cmd, 0, 0); - primitive_luatex("letterspacefont", letterspace_font_cmd, 0, 0); - primitive_luatex("expandglyphsinfont", normal_cmd, expand_font_code, 0); - primitive_luatex("copyfont", copy_font_cmd, 0, 0); - primitive_luatex("setfontid", set_font_id_cmd, 0, 0); - primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0); - primitive_tex("halign", halign_cmd, 0, 0); - primitive_tex("hrule", hrule_cmd, 0, 0); - primitive_luatex("nohrule", no_hrule_cmd, 0, 0); - primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0); - primitive_tex("insert", insert_cmd, 0, 0); - primitive_luatex("leftghost", char_ghost_cmd, 0, 0); - primitive_tex("mark", mark_cmd, 0, 0); - primitive_tex("mathaccent", math_accent_cmd, 0, 0); - primitive_luatex("Umathaccent", math_accent_cmd, 1, 0); - primitive_tex("mathchar", math_char_num_cmd, 0, 0); - primitive_luatex("Umathchar", math_char_num_cmd, 1, 0); - primitive_luatex("Umathcharnum", math_char_num_cmd, 2, 0); - primitive_tex("mathchoice", math_choice_cmd, 0, 0); - primitive_luatex("Ustack", math_choice_cmd, 1, 0); - primitive_tex("multiply", multiply_cmd, 0, 0); - primitive_tex("noalign", no_align_cmd, 0, 0); - primitive_tex("noboundary", boundary_cmd, 0, 0); - primitive_tex("boundary", boundary_cmd, 1, 0); - primitive_tex("protrusionboundary", boundary_cmd, 2, 0); - primitive_tex("wordboundary", boundary_cmd, 3, 0); - primitive_tex("noexpand", no_expand_cmd, 0, 0); - primitive_luatex("primitive", no_expand_cmd, 1, 0); - primitive_tex("nonscript", non_script_cmd, 0, 0); - primitive_tex("omit", omit_cmd, 0, 0); - primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc); - primitive_tex("penalty", break_penalty_cmd, 0, 0); - primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0); - primitive_tex("radical", radical_cmd, 0, 0); - primitive_luatex("Uradical", radical_cmd, 1, 0); - primitive_luatex("Uroot", radical_cmd, 2, 0); - primitive_luatex("Uunderdelimiter", radical_cmd, 3, 0); - primitive_luatex("Uoverdelimiter", radical_cmd, 4, 0); - primitive_luatex("Udelimiterunder", radical_cmd, 5, 0); - primitive_luatex("Udelimiterover", radical_cmd, 6, 0); - primitive_luatex("Uhextensible", radical_cmd, 7, 0); - primitive_tex("read", read_to_cs_cmd, 0, 0); - primitive_tex("relax", relax_cmd, too_big_char, too_big_char); - cs_text(frozen_relax) = maketexstring("relax"); - eqtb[frozen_relax] = eqtb[cur_val]; - primitive_luatex("rightghost", char_ghost_cmd, 1, 0); - primitive_tex("setbox", set_box_cmd, 0, 0); - primitive_tex("the", the_cmd, 0, 0); - primitive_luatex("toksapp", combine_toks_cmd, 0, 0); - primitive_luatex("tokspre", combine_toks_cmd, 1, 0); - primitive_luatex("etoksapp", combine_toks_cmd, 2, 0); - primitive_luatex("etokspre", combine_toks_cmd, 3, 0); - primitive_tex("toks", toks_register_cmd, 0, 0); - primitive_tex("vadjust", vadjust_cmd, 0, 0); - primitive_tex("valign", valign_cmd, 0, 0); - primitive_tex("vcenter", vcenter_cmd, 0, 0); - primitive_tex("vrule", vrule_cmd, 0, 0); - primitive_luatex("novrule", no_vrule_cmd, 0, 0); - primitive_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */ - par_loc = cur_val; - par_token = cs_token_flag + par_loc; - @; - @; - @; -} - - -@ These are in a separate module due to a CWEAVE limitation. - -@= - - /* - The processing of \.{\\input} involves the |start_input| subroutine, - which will be declared later; the processing of \.{\\endinput} is trivial. - */ - - primitive_tex("input", input_cmd, 0, 0); - primitive_tex("endinput", input_cmd, 1, 0); - primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0); - primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0); - primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0); - primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0); - primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0); - primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0); - primitive_etex("marks", mark_cmd, marks_code, 0); - primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0); - primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, 0); - primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0); - primitive_etex("splitfirstmarks", top_bot_mark_cmd, split_first_mark_code + marks_code, 0); - primitive_etex("splitbotmarks", top_bot_mark_cmd, split_bot_mark_code + marks_code, 0); - - /* - The hash table is initialized with `\.{\\count}', `\.{\\attribute}', - `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register| - as their command code; they are distinguished by the |chr_code|, which - is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|. - */ - - primitive_tex("count", register_cmd, int_val_level, 0); - primitive_luatex("attribute", register_cmd, attr_val_level, 0); - primitive_tex("dimen", register_cmd, dimen_val_level, 0); - primitive_tex("skip", register_cmd, glue_val_level, 0); - primitive_tex("muskip", register_cmd, mu_val_level, 0); - - primitive_tex("spacefactor", set_aux_cmd, hmode, 0); - primitive_tex("prevdepth", set_aux_cmd, vmode, 0); - primitive_tex("deadcycles", set_page_int_cmd, 0, 0); - primitive_tex("insertpenalties", set_page_int_cmd, 1, 0); - primitive_tex("wd", set_box_dimen_cmd, width_offset, 0); - primitive_tex("ht", set_box_dimen_cmd, height_offset, 0); - primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0); - primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0); - primitive_tex("lastkern", last_item_cmd, lastkern_code, 0); - primitive_tex("lastskip", last_item_cmd, lastskip_code, 0); - primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0); - primitive_tex("badness", last_item_cmd, badness_code, 0); - primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0); - primitive_luatex("lastsavedboxresourceindex", last_item_cmd, last_saved_box_resource_index_code, 0); - primitive_luatex("lastsavedimageresourceindex", last_item_cmd, last_saved_image_resource_index_code, 0); - primitive_luatex("lastsavedimageresourcepages", last_item_cmd, last_saved_image_resource_pages_code, 0); - primitive_luatex("lastxpos", last_item_cmd, last_x_pos_code, 0); - primitive_luatex("lastypos", last_item_cmd, last_y_pos_code, 0); - primitive_luatex("randomseed", last_item_cmd, random_seed_code, 0); - - primitive_tex("number", convert_cmd, number_code, 0); - primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0); - primitive_tex("string", convert_cmd, string_code, 0); - primitive_tex("csstring", convert_cmd, cs_string_code, 0); - primitive_tex("meaning", convert_cmd, meaning_code, 0); - primitive_etex("eTeXVersion", convert_cmd, etex_code, 0); - primitive_tex("fontname", convert_cmd, font_name_code, 0); - primitive_luatex("fontid", convert_cmd, font_id_code, 0); - primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0); - primitive_luatex("luatexdatestamp", convert_cmd, luatex_date_code, 0); - primitive_luatex("luatexbanner", convert_cmd, luatex_banner_code, 0); - primitive_luatex("leftmarginkern", convert_cmd, left_margin_kern_code, 0); - primitive_luatex("rightmarginkern", convert_cmd, right_margin_kern_code, 0); - primitive_luatex("uniformdeviate", convert_cmd, uniform_deviate_code, 0); - primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0); - primitive_core("directlua", convert_cmd, lua_code, 0); - primitive_luatex("luafunction", convert_cmd, lua_function_code, 0); - primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0); - primitive_luatex("mathstyle", convert_cmd, math_style_code, 0); - primitive_luatex("expanded", convert_cmd, expanded_code, 0); - primitive_tex("jobname", convert_cmd, job_name_code, 0); - primitive_luatex("formatname", convert_cmd, format_name_code, 0); - primitive_luatex("Uchar", convert_cmd, uchar_code, 0); - - primitive_luatex("Umathcharclass", convert_cmd, math_char_class_code, 0); - primitive_luatex("Umathcharfam", convert_cmd, math_char_fam_code, 0); - primitive_luatex("Umathcharslot", convert_cmd, math_char_slot_code, 0); - - primitive_tex("if", if_test_cmd, if_char_code, 0); - primitive_tex("ifcat", if_test_cmd, if_cat_code, 0); - primitive_tex("ifnum", if_test_cmd, if_int_code, 0); - primitive_tex("ifdim", if_test_cmd, if_dim_code, 0); - primitive_tex("ifodd", if_test_cmd, if_odd_code, 0); - primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0); - primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0); - primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0); - primitive_tex("ifinner", if_test_cmd, if_inner_code, 0); - primitive_tex("ifvoid", if_test_cmd, if_void_code, 0); - - primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0); - primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0); - primitive_tex("ifx", if_test_cmd, ifx_code, 0); - primitive_tex("ifeof", if_test_cmd, if_eof_code, 0); - primitive_tex("iftrue", if_test_cmd, if_true_code, 0); - primitive_tex("iffalse", if_test_cmd, if_false_code, 0); - primitive_tex("ifcase", if_test_cmd, if_case_code, 0); - primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0); - primitive_tex("fi", fi_or_else_cmd, fi_code, 0); - cs_text(frozen_fi) = maketexstring("fi"); - eqtb[frozen_fi] = eqtb[cur_val]; - primitive_tex("or", fi_or_else_cmd, or_code, 0); - primitive_tex("else", fi_or_else_cmd, else_code, 0); - - /* - \TeX\ always knows at least one font, namely the null font. It has no - characters, and its seven parameters are all equal to zero. - */ - - primitive_tex("nullfont", set_font_cmd, null_font, 0); - cs_text(frozen_null_font) = maketexstring("nullfont"); - eqtb[frozen_null_font] = eqtb[cur_val]; - - primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code); - primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, tab_mark_cmd_code); - primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, tab_mark_cmd_code); - primitive_tex("cr", car_ret_cmd, cr_code, cr_code); - cs_text(frozen_cr) = maketexstring("cr"); - eqtb[frozen_cr] = eqtb[cur_val]; - primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code); - cs_text(frozen_end_template) = maketexstring("endtemplate"); - cs_text(frozen_endv) = maketexstring("endtemplate"); - set_eq_type(frozen_endv, endv_cmd); - set_equiv(frozen_endv, null_list); - set_eq_level(frozen_endv, level_one); - eqtb[frozen_end_template] = eqtb[frozen_endv]; - set_eq_type(frozen_end_template, end_template_cmd); - - primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0); - primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0); - primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0); - primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0); - primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0); - primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0); - primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0); - primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0); - - /* - Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the - endgame, since both of them have `|stop|' as their command code. - */ - - primitive_tex("end", stop_cmd, 0, 0); - primitive_tex("dump", stop_cmd, 1, 0); - - primitive_tex("hskip", hskip_cmd, skip_code, 0); - primitive_tex("hfil", hskip_cmd, fil_code, 0); - primitive_tex("hfill", hskip_cmd, fill_code, 0); - primitive_tex("hss", hskip_cmd, ss_code, 0); - primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0); - primitive_tex("vskip", vskip_cmd, skip_code, 0); - primitive_tex("vfil", vskip_cmd, fil_code, 0); - primitive_tex("vfill", vskip_cmd, fill_code, 0); - primitive_tex("vss", vskip_cmd, ss_code, 0); - primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0); - primitive_tex("mskip", mskip_cmd, mskip_code, 0); - primitive_tex("kern", kern_cmd, explicit_kern, 0); - primitive_tex("mkern", mkern_cmd, mu_glue, 0); - primitive_tex("moveleft", hmove_cmd, 1, 0); - primitive_tex("moveright", hmove_cmd, 0, 0); - primitive_tex("raise", vmove_cmd, 1, 0); - primitive_tex("lower", vmove_cmd, 0, 0); - primitive_tex("box", make_box_cmd, box_code, 0); - primitive_tex("copy", make_box_cmd, copy_code, 0); - primitive_tex("lastbox", make_box_cmd, last_box_code, 0); - primitive_tex("vsplit", make_box_cmd, vsplit_code, 0); - primitive_tex("tpack", make_box_cmd, tpack_code, 0); - primitive_tex("vpack", make_box_cmd, vpack_code, 0); - primitive_tex("hpack", make_box_cmd, hpack_code, 0); - primitive_tex("vtop", make_box_cmd, vtop_code, 0); - primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0); - primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0); - primitive_tex("shipout", leader_ship_cmd, a_leaders - 1, 0); /* |ship_out_flag=leader_flag-1| */ - primitive_tex("leaders", leader_ship_cmd, a_leaders, 0); - primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0); - primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0); - primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0); - primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0); - primitive_tex("indent", start_par_cmd, 1, 0); - primitive_tex("noindent", start_par_cmd, 0, 0); - primitive_luatex("quitvmode", start_par_cmd, 2, 0); - primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0); - primitive_tex("unkern", remove_item_cmd, kern_node, 0); - primitive_tex("unskip", remove_item_cmd, glue_node, 0); - primitive_tex("unhbox", un_hbox_cmd, box_code, 0); - primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0); - primitive_tex("unvbox", un_vbox_cmd, box_code, 0); - primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0); - primitive_tex("-", discretionary_cmd, explicit_disc, 0); - primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0); - primitive_luatex("localleftbox", assign_local_box_cmd, 0, 0); - primitive_luatex("localrightbox", assign_local_box_cmd, 1, 0); - - primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0); - primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0); - primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0); - primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, cramped_display_style, 0); - primitive_tex("eqno", eq_no_cmd, 0, 0); - primitive_tex("leqno", eq_no_cmd, 1, 0); - primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0); - primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0); - primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0); - primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0); - primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0); - primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0); - primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0); - primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0); - primitive_tex("underline", math_comp_cmd, under_noad_type, 0); - primitive_tex("overline", math_comp_cmd, over_noad_type, 0); - primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0); - primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0); - primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0); - primitive_tex("displaystyle", math_style_cmd, display_style, 0); - primitive_tex("textstyle", math_style_cmd, text_style, 0); - primitive_tex("scriptstyle", math_style_cmd, script_style, 0); - primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0); - primitive_luatex("crampeddisplaystyle", math_style_cmd, cramped_display_style, 0); - primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0); - primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, 0); - primitive_luatex("crampedscriptscriptstyle", math_style_cmd, cramped_script_script_style, 0); - primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd); - primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd); - primitive_tex("above", above_cmd, above_code, 0); - primitive_tex("over", above_cmd, over_code, 0); - primitive_tex("atop", above_cmd, atop_code, 0); - primitive_luatex("Uskewed", above_cmd, skewed_code, 0); - primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0); - primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0); - primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0); - primitive_luatex("Uskewedwithdelims", above_cmd, delimited_code + skewed_code, 0); - primitive_tex("left", left_right_cmd, left_noad_side, 0); - primitive_tex("right", left_right_cmd, right_noad_side, 0); - primitive_tex("middle", left_right_cmd, middle_noad_side, 0); - primitive_tex("Uleft", left_right_cmd, 10+left_noad_side, 0); - primitive_tex("Uright", left_right_cmd, 10+right_noad_side, 0); - primitive_tex("Umiddle", left_right_cmd, 10+middle_noad_side, 0); - primitive_luatex("Uvextensible", left_right_cmd, 10+no_noad_side, 0); - cs_text(frozen_right) = maketexstring("right"); - eqtb[frozen_right] = eqtb[cur_val]; - - primitive_tex("long", prefix_cmd, 1, 0); - primitive_tex("outer", prefix_cmd, 2, 0); - primitive_tex("global", prefix_cmd, 4, 0); - primitive_tex("def", def_cmd, 0, 0); - primitive_tex("gdef", def_cmd, 1, 0); - primitive_tex("edef", def_cmd, 2, 0); - primitive_tex("xdef", def_cmd, 3, 0); - primitive_tex("let", let_cmd, normal, 0); - primitive_tex("futurelet", let_cmd, normal + 1, 0); - primitive_luatex("letcharcode", let_cmd, normal + 2, 0); - primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0); - primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0); - primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0); - primitive_luatex("Umathcharnumdef", shorthand_def_cmd, umath_char_def_code, 0); - primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0); - primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0); - primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0); - primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0); - primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0); - primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0); - primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base); - primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base); - primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base); - primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base); - primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base); - primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base); - primitive_tex("textfont", def_family_cmd, text_size, 0); - primitive_tex("scriptfont", def_family_cmd, script_size, 0); - primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0); - primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); - primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); - -@ These are in a separate module due to a CWEAVE limitation. - -@= - primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0); - primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0); - primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0); - primitive_luatex("Umathoverbarvgap", set_math_param_cmd, math_param_overbar_vgap, 0); - primitive_luatex("Umathunderbarkern", set_math_param_cmd, math_param_underbar_kern, 0); - primitive_luatex("Umathunderbarrule", set_math_param_cmd, math_param_underbar_rule, 0); - primitive_luatex("Umathunderbarvgap", set_math_param_cmd, math_param_underbar_vgap, 0); - primitive_luatex("Umathradicalkern", set_math_param_cmd, math_param_radical_kern, 0); - primitive_luatex("Umathradicalrule", set_math_param_cmd, math_param_radical_rule, 0); - primitive_luatex("Umathradicalvgap", set_math_param_cmd, math_param_radical_vgap, 0); - primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, math_param_radical_degree_before, 0); - primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, math_param_radical_degree_after, 0); - primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, math_param_radical_degree_raise, 0); - primitive_luatex("Umathstackvgap", set_math_param_cmd, math_param_stack_vgap, 0); - primitive_luatex("Umathstacknumup", set_math_param_cmd, math_param_stack_num_up, 0); - primitive_luatex("Umathstackdenomdown", set_math_param_cmd, math_param_stack_denom_down, 0); - primitive_luatex("Umathfractionrule", set_math_param_cmd, math_param_fraction_rule, 0); - primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, math_param_fraction_num_vgap, 0); - primitive_luatex("Umathfractionnumup", set_math_param_cmd, math_param_fraction_num_up, 0); - primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, math_param_fraction_denom_vgap, 0); - primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, math_param_fraction_denom_down, 0); - primitive_luatex("Umathfractiondelsize", set_math_param_cmd, math_param_fraction_del_size, 0); - primitive_luatex("Umathskewedfractionvgap", set_math_param_cmd, math_param_skewed_fraction_vgap, 0); - primitive_luatex("Umathskewedfractionhgap", set_math_param_cmd, math_param_skewed_fraction_hgap, 0); - primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, math_param_limit_above_vgap, 0); - primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, math_param_limit_above_bgap, 0); - primitive_luatex("Umathlimitabovekern", set_math_param_cmd, math_param_limit_above_kern, 0); - primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, math_param_limit_below_vgap, 0); - primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, math_param_limit_below_bgap, 0); - primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, math_param_limit_below_kern, 0); - primitive_luatex("Umathnolimitsubfactor", set_math_param_cmd, math_param_nolimit_sub_factor, 0); /* bonus */ - primitive_luatex("Umathnolimitsupfactor", set_math_param_cmd, math_param_nolimit_sup_factor, 0); /* bonus */ - primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, math_param_under_delimiter_vgap, 0); - primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, math_param_under_delimiter_bgap, 0); - primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, math_param_over_delimiter_vgap, 0); - primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, math_param_over_delimiter_bgap, 0); - primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, math_param_sub_shift_drop, 0); - primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, math_param_sup_shift_drop, 0); - primitive_luatex("Umathsubshiftdown", set_math_param_cmd, math_param_sub_shift_down, 0); - primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, math_param_sub_sup_shift_down, 0); - primitive_luatex("Umathsubtopmax", set_math_param_cmd, math_param_sub_top_max, 0); - primitive_luatex("Umathsupshiftup", set_math_param_cmd, math_param_sup_shift_up, 0); - primitive_luatex("Umathsupbottommin", set_math_param_cmd, math_param_sup_bottom_min, 0); - primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, math_param_sup_sub_bottom_max, 0); - primitive_luatex("Umathsubsupvgap", set_math_param_cmd, math_param_subsup_vgap, 0); - primitive_luatex("Umathspaceafterscript", set_math_param_cmd, math_param_space_after_script, 0); - primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, math_param_connector_overlap_min, 0); - primitive_luatex("Umathordordspacing", set_math_param_cmd, math_param_ord_ord_spacing, 0); - primitive_luatex("Umathordopspacing", set_math_param_cmd, math_param_ord_op_spacing, 0); - primitive_luatex("Umathordbinspacing", set_math_param_cmd, math_param_ord_bin_spacing, 0); - primitive_luatex("Umathordrelspacing", set_math_param_cmd, math_param_ord_rel_spacing, 0); - primitive_luatex("Umathordopenspacing", set_math_param_cmd, math_param_ord_open_spacing, 0); - primitive_luatex("Umathordclosespacing", set_math_param_cmd, math_param_ord_close_spacing, 0); - primitive_luatex("Umathordpunctspacing", set_math_param_cmd, math_param_ord_punct_spacing, 0); - primitive_luatex("Umathordinnerspacing", set_math_param_cmd, math_param_ord_inner_spacing, 0); - primitive_luatex("Umathopordspacing", set_math_param_cmd, math_param_op_ord_spacing, 0); - primitive_luatex("Umathopopspacing", set_math_param_cmd, math_param_op_op_spacing, 0); - primitive_luatex("Umathopbinspacing", set_math_param_cmd, math_param_op_bin_spacing, 0); - primitive_luatex("Umathoprelspacing", set_math_param_cmd, math_param_op_rel_spacing, 0); - primitive_luatex("Umathopopenspacing", set_math_param_cmd, math_param_op_open_spacing, 0); - primitive_luatex("Umathopclosespacing", set_math_param_cmd, math_param_op_close_spacing, 0); - primitive_luatex("Umathoppunctspacing", set_math_param_cmd, math_param_op_punct_spacing, 0); - primitive_luatex("Umathopinnerspacing", set_math_param_cmd, math_param_op_inner_spacing, 0); - primitive_luatex("Umathbinordspacing", set_math_param_cmd, math_param_bin_ord_spacing, 0); - primitive_luatex("Umathbinopspacing", set_math_param_cmd, math_param_bin_op_spacing, 0); - primitive_luatex("Umathbinbinspacing", set_math_param_cmd, math_param_bin_bin_spacing, 0); - primitive_luatex("Umathbinrelspacing", set_math_param_cmd, math_param_bin_rel_spacing, 0); - primitive_luatex("Umathbinopenspacing", set_math_param_cmd, math_param_bin_open_spacing, 0); - primitive_luatex("Umathbinclosespacing", set_math_param_cmd, math_param_bin_close_spacing, 0); - primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, math_param_bin_punct_spacing, 0); - primitive_luatex("Umathbininnerspacing", set_math_param_cmd, math_param_bin_inner_spacing, 0); - primitive_luatex("Umathrelordspacing", set_math_param_cmd, math_param_rel_ord_spacing, 0); - primitive_luatex("Umathrelopspacing", set_math_param_cmd, math_param_rel_op_spacing, 0); - primitive_luatex("Umathrelbinspacing", set_math_param_cmd, math_param_rel_bin_spacing, 0); - primitive_luatex("Umathrelrelspacing", set_math_param_cmd, math_param_rel_rel_spacing, 0); - primitive_luatex("Umathrelopenspacing", set_math_param_cmd, math_param_rel_open_spacing, 0); - primitive_luatex("Umathrelclosespacing", set_math_param_cmd, math_param_rel_close_spacing, 0); - primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, math_param_rel_punct_spacing, 0); - primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, math_param_rel_inner_spacing, 0); - primitive_luatex("Umathopenordspacing", set_math_param_cmd, math_param_open_ord_spacing, 0); - primitive_luatex("Umathopenopspacing", set_math_param_cmd, math_param_open_op_spacing, 0); - primitive_luatex("Umathopenbinspacing", set_math_param_cmd, math_param_open_bin_spacing, 0); - primitive_luatex("Umathopenrelspacing", set_math_param_cmd, math_param_open_rel_spacing, 0); - primitive_luatex("Umathopenopenspacing", set_math_param_cmd, math_param_open_open_spacing, 0); - primitive_luatex("Umathopenclosespacing", set_math_param_cmd, math_param_open_close_spacing, 0); - primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, math_param_open_punct_spacing, 0); - primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, math_param_open_inner_spacing, 0); - primitive_luatex("Umathcloseordspacing", set_math_param_cmd, math_param_close_ord_spacing, 0); - primitive_luatex("Umathcloseopspacing", set_math_param_cmd, math_param_close_op_spacing, 0); - primitive_luatex("Umathclosebinspacing", set_math_param_cmd, math_param_close_bin_spacing, 0); - primitive_luatex("Umathcloserelspacing", set_math_param_cmd, math_param_close_rel_spacing, 0); - primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, math_param_close_open_spacing, 0); - primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, math_param_close_close_spacing, 0); - primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, math_param_close_punct_spacing, 0); - primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, math_param_close_inner_spacing, 0); - primitive_luatex("Umathpunctordspacing", set_math_param_cmd, math_param_punct_ord_spacing, 0); - primitive_luatex("Umathpunctopspacing", set_math_param_cmd, math_param_punct_op_spacing, 0); - primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, math_param_punct_bin_spacing, 0); - primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, math_param_punct_rel_spacing, 0); - primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, math_param_punct_open_spacing, 0); - primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, math_param_punct_close_spacing, 0); - primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, math_param_punct_punct_spacing, 0); - primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, math_param_punct_inner_spacing, 0); - primitive_luatex("Umathinnerordspacing", set_math_param_cmd, math_param_inner_ord_spacing, 0); - primitive_luatex("Umathinneropspacing", set_math_param_cmd, math_param_inner_op_spacing, 0); - primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, math_param_inner_bin_spacing, 0); - primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, math_param_inner_rel_spacing, 0); - primitive_luatex("Umathinneropenspacing", set_math_param_cmd, math_param_inner_open_spacing, 0); - primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, math_param_inner_close_spacing, 0); - primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0); - primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0); - -@ These are in a separate module due to a CWEAVE limitation. - -@= - primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base); - primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base); - primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base); - primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 1, del_code_base); - primitive_tex("hyphenation", hyph_data_cmd, 0, 0); - primitive_tex("patterns", hyph_data_cmd, 1, 0); - primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0); - primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0); - primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0); - primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0); - primitive_luatex("hyphenationmin", hyph_data_cmd, 6, 0); - primitive_luatex("hjcode", hyph_data_cmd, 7, 0); - primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0); - primitive_tex("skewchar", assign_font_int_cmd, 1, 0); - primitive_luatex("lpcode", assign_font_int_cmd, lp_code_base, 0); - primitive_luatex("rpcode", assign_font_int_cmd, rp_code_base, 0); - primitive_luatex("efcode", assign_font_int_cmd, ef_code_base, 0); - primitive_luatex("tagcode", assign_font_int_cmd, tag_code, 0); - primitive_luatex("ignoreligaturesinfont", assign_font_int_cmd, no_lig_code, 0); - primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0); - primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0); - primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0); - primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0); - primitive_tex("openin", in_stream_cmd, 1, 0); - primitive_tex("closein", in_stream_cmd, 0, 0); - primitive_tex("message", message_cmd, 0, 0); - primitive_tex("errmessage", message_cmd, 1, 0); - primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base); - primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base); - primitive_tex("show", xray_cmd, show_code, 0); - primitive_tex("showbox", xray_cmd, show_box_code, 0); - primitive_tex("showthe", xray_cmd, show_the_code, 0); - primitive_tex("showlists", xray_cmd, show_lists, 0); - - primitive_tex("openout", extension_cmd, open_code, 0); - primitive_tex("write", extension_cmd, write_code, 0); - write_loc = cur_val; - primitive_tex("closeout", extension_cmd, close_code, 0); - primitive_tex("special", extension_cmd, special_code, 0); - cs_text(frozen_special) = maketexstring("special"); - eqtb[frozen_special] = eqtb[cur_val]; - primitive_tex("immediate", extension_cmd, immediate_code, 0); - primitive_luatex("localinterlinepenalty", assign_int_cmd, int_base + local_inter_line_penalty_code, int_base); - primitive_luatex("localbrokenpenalty", assign_int_cmd, int_base + local_broken_penalty_code, int_base); - primitive_luatex("pagedir", assign_dir_cmd, int_base + page_direction_code, dir_base); - primitive_luatex("bodydir", assign_dir_cmd, int_base + body_direction_code, dir_base); - primitive_luatex("pardir", assign_dir_cmd, int_base + par_direction_code, dir_base); - primitive_luatex("textdir", assign_dir_cmd, int_base + text_direction_code, dir_base); - primitive_luatex("mathdir", assign_dir_cmd, int_base + math_direction_code, dir_base); - primitive_luatex("linedir", assign_dir_cmd, int_base + line_direction_code, dir_base); - primitive_luatex("pageleftoffset", assign_dimen_cmd, dimen_base + page_left_offset_code, dimen_base); - primitive_luatex("pagetopoffset", assign_dimen_cmd, dimen_base + page_top_offset_code, dimen_base); - primitive_luatex("pagerightoffset", assign_dimen_cmd, dimen_base + page_right_offset_code, dimen_base); - primitive_luatex("pagebottomoffset", assign_dimen_cmd, dimen_base + page_bottom_offset_code, dimen_base); - primitive_luatex("saveboxresource", extension_cmd, save_box_resource_code, 0); - primitive_luatex("useboxresource", extension_cmd, use_box_resource_code, 0); - primitive_luatex("saveimageresource", extension_cmd, save_image_resource_code, 0); - primitive_luatex("useimageresource", extension_cmd, use_image_resource_code, 0); - primitive_luatex("savepos", normal_cmd, save_pos_code, 0); - primitive_luatex("savecatcodetable", normal_cmd, save_cat_code_table_code, 0); - primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0); - primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0); - primitive_luatex("latelua", normal_cmd, late_lua_code, 0); - primitive_luatex("insertht", convert_cmd, insert_ht_code, 0); - primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0); - primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0); - primitive_luatex("dvivariable", variable_cmd, dvi_variable_code, 0); - primitive_luatex("pdfextension", extension_cmd, pdf_extension_code, 0); - primitive_luatex("pdffeedback", feedback_cmd, pdf_feedback_code, 0); - primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0); - primitive_luatex("mathoption", option_cmd, math_option_code, 0); - - /* - some of the internal integer parameters are not associated with actual - primitives at all. - */ - - primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base); - primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base); - - -@ @c -void initialize_etex_commands(void) -{ - primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0); - primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0); - primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0); - primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0); - - /* - First we implement the additional \eTeX\ parameters in the table of equivalents. - */ - - primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); - primitive_etex("tracingassigns", assign_int_cmd, int_base + tracing_assigns_code, int_base); - primitive_etex("tracinggroups", assign_int_cmd, int_base + tracing_groups_code, int_base); - primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, int_base); - primitive_etex("tracingscantokens", assign_int_cmd, int_base + tracing_scan_tokens_code, int_base); - primitive_etex("tracingnesting", assign_int_cmd, int_base + tracing_nesting_code, int_base); - primitive_etex("predisplaydirection", assign_int_cmd, int_base + pre_display_direction_code, int_base); - primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, int_base); - primitive_etex("savingvdiscards", assign_int_cmd, int_base + saving_vdiscards_code, int_base); - primitive_etex("savinghyphcodes", assign_int_cmd, int_base + saving_hyph_codes_code, int_base); - primitive_luatex("suppressfontnotfounderror", assign_int_cmd, int_base + suppress_fontnotfound_error_code, int_base); - primitive_luatex("suppresslongerror", assign_int_cmd, int_base + suppress_long_error_code, int_base); - primitive_luatex("suppressmathparerror", assign_int_cmd, int_base + suppress_mathpar_error_code, int_base); - primitive_luatex("suppressifcsnameerror", assign_int_cmd, int_base + suppress_ifcsname_error_code, int_base); - primitive_luatex("suppressoutererror", assign_int_cmd, int_base + suppress_outer_error_code, int_base); - primitive_luatex("matheqnogapstep", assign_int_cmd, int_base + math_eqno_gap_step_code, int_base); - primitive_luatex("mathdisplayskipmode", assign_int_cmd, int_base + math_display_skip_mode_code, int_base); - primitive_luatex("mathscriptsmode", assign_int_cmd, int_base + math_scripts_mode_code, int_base); - primitive_luatex("mathnolimitsmode", assign_int_cmd, int_base + math_nolimits_mode_code, int_base); - primitive_luatex("mathrulesmode", assign_int_cmd, int_base + math_rules_mode_code, int_base); - primitive_luatex("mathrulesfam", assign_int_cmd, int_base + math_rules_fam_code, int_base); - primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, int_base); - - primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, 0); - primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, 0); - - primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0); - primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0); - primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0); - primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0); - primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0); - primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0); - primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0); - - primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0); - primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0); - primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0); - - primitive_luatex("shapemode", assign_int_cmd, int_base + shape_mode_code, int_base); - primitive_luatex("hyphenationbounds", assign_int_cmd, int_base + hyphenation_bounds_code, int_base); - - primitive_etex("showgroups", xray_cmd, show_groups, 0); - - /* - The \.{\\showtokens} command displays a token list. - */ - - primitive_etex("showtokens", xray_cmd, show_tokens, 0); - - /* - The \.{\\unexpanded} primitive prevents expansion of tokens much as - the result from \.{\\the} applied to a token variable. The - \.{\\detokenize} primitive converts a token list into a list of - character tokens much as if the token list were written to a file. We - use the fact that the command modifiers for \.{\\unexpanded} and - \.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe} - are even. - */ - - primitive_etex("unexpanded", the_cmd, 1, 0); - primitive_etex("detokenize", the_cmd, show_tokens, 0); - - /* - The \.{\\showifs} command displays all currently active conditionals. - */ - - primitive_etex("showifs", xray_cmd, show_ifs, 0); - - /* - The \.{\\interactionmode} primitive allows to query and set the interaction mode. - */ - - primitive_etex("interactionmode", set_page_int_cmd, 2, 0); - - /* - The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. - */ - - primitive_etex("scantokens", input_cmd, 2, 0); - primitive_luatex("scantextokens", input_cmd, 3, 0); - - primitive_etex("readline", read_to_cs_cmd, 1, 0); - - primitive_etex("unless", expand_after_cmd, 1, 0); - primitive_etex("ifdefined", if_test_cmd, if_def_code, 0); - primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0); - primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0); - primitive_luatex("ifincsname", if_test_cmd, if_in_csname_code, 0); - primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0); - primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0); - - /* - The |protected| feature of \eTeX\ defines the \.{\\protected} prefix - command for macro definitions. Such macros are protected against - expansions when lists of expanded tokens are built, e.g., for \.{\\edef} - or during \.{\\write}. - */ - - primitive_etex("protected", prefix_cmd, 8, 0); - - /* - Here are the additional \eTeX\ primitives for expressions. - */ - - primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0); - primitive_etex("dimexpr", last_item_cmd, eTeX_expr - int_val_level + dimen_val_level, 0); - primitive_etex("glueexpr", last_item_cmd, eTeX_expr - int_val_level + glue_val_level, 0); - primitive_etex("muexpr", last_item_cmd, eTeX_expr - int_val_level + mu_val_level, 0); - - primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, 0); - primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0); - primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0); - primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0); - - primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0); - primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0); - - /* - The \.{\\pagediscards} and \.{\\splitdiscards} commands share the - command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are - distinguished by their |chr_code| values |last_box_code| and - |vsplit_code|. These |chr_code| values are larger than |box_code| and - |copy_code|. - */ - - primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0); - primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0); - - /* - The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, - and \.{\\displaywidowpenalties} commands allow to define arrays of - penalty values to be used instead of the corresponding single values. - */ - - primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base); - primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, etex_pen_base); - primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, etex_pen_base); - primitive_etex("displaywidowpenalties", set_etex_shape_cmd, display_widow_penalties_loc, etex_pen_base); - -} +% commands.w +% +% Copyright 2009-2010 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +\def\eTeX{e-\TeX} + +@ @c + + +#include "ptexlib.h" + +@ 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. + +@c +void initialize_commands(void) +{ + + primitive_tex("lineskip", assign_glue_cmd, glue_base + line_skip_code, glue_base); + primitive_tex("baselineskip", assign_glue_cmd, glue_base + baseline_skip_code, glue_base); + primitive_tex("parskip", assign_glue_cmd, glue_base + par_skip_code, glue_base); + primitive_tex("abovedisplayskip", assign_glue_cmd, glue_base + above_display_skip_code, glue_base); + primitive_tex("belowdisplayskip", assign_glue_cmd, glue_base + below_display_skip_code, glue_base); + primitive_tex("abovedisplayshortskip", assign_glue_cmd, glue_base + above_display_short_skip_code, glue_base); + primitive_tex("belowdisplayshortskip", assign_glue_cmd, glue_base + below_display_short_skip_code, glue_base); + primitive_tex("leftskip", assign_glue_cmd, glue_base + left_skip_code, glue_base); + primitive_tex("rightskip", assign_glue_cmd, glue_base + right_skip_code, glue_base); + primitive_tex("topskip", assign_glue_cmd, glue_base + top_skip_code, glue_base); + primitive_tex("splittopskip", assign_glue_cmd, glue_base + split_top_skip_code, glue_base); + primitive_tex("tabskip", assign_glue_cmd, glue_base + tab_skip_code, glue_base); + primitive_tex("spaceskip", assign_glue_cmd, glue_base + space_skip_code, glue_base); + primitive_tex("xspaceskip", assign_glue_cmd, glue_base + xspace_skip_code, glue_base); + primitive_tex("parfillskip", assign_glue_cmd, glue_base + par_fill_skip_code, glue_base); + primitive_tex("thinmuskip", assign_mu_glue_cmd, glue_base + thin_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_tex("medmuskip", assign_mu_glue_cmd, glue_base + med_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_tex("thickmuskip", assign_mu_glue_cmd, glue_base + thick_mu_skip_code, glue_base + thin_mu_skip_code); + primitive_luatex("mathsurroundskip", assign_glue_cmd, glue_base + math_skip_code, glue_base); + primitive_luatex("mathsurroundmode", assign_int_cmd, int_base + math_skip_mode_code, int_base); + primitive_tex("output", assign_toks_cmd, output_routine_loc, local_base); + primitive_tex("everypar", assign_toks_cmd, every_par_loc, local_base); + primitive_tex("everymath", assign_toks_cmd, every_math_loc, local_base); + primitive_tex("everydisplay", assign_toks_cmd, every_display_loc, local_base); + primitive_tex("everyhbox", assign_toks_cmd, every_hbox_loc, local_base); + primitive_tex("everyvbox", assign_toks_cmd, every_vbox_loc, local_base); + primitive_tex("everyjob", assign_toks_cmd, every_job_loc, local_base); + primitive_tex("everycr", assign_toks_cmd, every_cr_loc, local_base); + primitive_tex("errhelp", assign_toks_cmd, err_help_loc, local_base); + + /* The integer parameter names must be entered into the hash table */ + + primitive_tex("pretolerance", assign_int_cmd, int_base + pretolerance_code, int_base); + primitive_tex("tolerance", assign_int_cmd, int_base + tolerance_code, int_base); + primitive_tex("linepenalty", assign_int_cmd, int_base + line_penalty_code, int_base); + primitive_tex("hyphenpenalty", assign_int_cmd, int_base + hyphen_penalty_code, int_base); + primitive_tex("exhyphenpenalty", assign_int_cmd, int_base + ex_hyphen_penalty_code, int_base); + primitive_tex("clubpenalty", assign_int_cmd, int_base + club_penalty_code, int_base); + primitive_tex("widowpenalty", assign_int_cmd, int_base + widow_penalty_code, int_base); + primitive_tex("displaywidowpenalty", assign_int_cmd, int_base + display_widow_penalty_code, int_base); + primitive_tex("brokenpenalty", assign_int_cmd, int_base + broken_penalty_code, int_base); + primitive_tex("binoppenalty", assign_int_cmd, int_base + bin_op_penalty_code, int_base); + primitive_tex("relpenalty", assign_int_cmd, int_base + rel_penalty_code, int_base); + primitive_tex("predisplaypenalty", assign_int_cmd, int_base + pre_display_penalty_code, int_base); + primitive_tex("postdisplaypenalty", assign_int_cmd, int_base + post_display_penalty_code, int_base); + primitive_tex("interlinepenalty", assign_int_cmd, int_base + inter_line_penalty_code, int_base); + primitive_tex("doublehyphendemerits", assign_int_cmd, int_base + double_hyphen_demerits_code, int_base); + primitive_tex("finalhyphendemerits", assign_int_cmd, int_base + final_hyphen_demerits_code, int_base); + primitive_tex("adjdemerits", assign_int_cmd, int_base + adj_demerits_code, int_base); + primitive_tex("mag", assign_int_cmd, int_base + mag_code, int_base); + primitive_tex("delimiterfactor", assign_int_cmd, int_base + delimiter_factor_code, int_base); + primitive_tex("looseness", assign_int_cmd, int_base + looseness_code, int_base); + primitive_tex("time", assign_int_cmd, int_base + time_code, int_base); + primitive_tex("day", assign_int_cmd, int_base + day_code, int_base); + primitive_tex("month", assign_int_cmd, int_base + month_code, int_base); + primitive_tex("year", assign_int_cmd, int_base + year_code, int_base); + primitive_tex("showboxbreadth", assign_int_cmd, int_base + show_box_breadth_code, int_base); + primitive_tex("showboxdepth", assign_int_cmd, int_base + show_box_depth_code, int_base); + primitive_tex("hbadness", assign_int_cmd, int_base + hbadness_code, int_base); + primitive_tex("vbadness", assign_int_cmd, int_base + vbadness_code, int_base); + primitive_tex("pausing", assign_int_cmd, int_base + pausing_code, int_base); + primitive_tex("tracingonline", assign_int_cmd, int_base + tracing_online_code, int_base); + primitive_tex("tracingmacros", assign_int_cmd, int_base + tracing_macros_code, int_base); + primitive_tex("tracingstats", assign_int_cmd, int_base + tracing_stats_code, int_base); + primitive_tex("tracingparagraphs", assign_int_cmd, int_base + tracing_paragraphs_code, int_base); + primitive_tex("tracingpages", assign_int_cmd, int_base + tracing_pages_code, int_base); + primitive_tex("tracingoutput", assign_int_cmd, int_base + tracing_output_code, int_base); + primitive_tex("tracinglostchars", assign_int_cmd, int_base + tracing_lost_chars_code, int_base); + primitive_tex("tracingcommands", assign_int_cmd, int_base + tracing_commands_code, int_base); + primitive_tex("tracingrestores", assign_int_cmd, int_base + tracing_restores_code, int_base); + primitive_tex("uchyph", assign_int_cmd, int_base + uc_hyph_code, int_base); + primitive_tex("outputpenalty", assign_int_cmd, int_base + output_penalty_code, int_base); + primitive_tex("maxdeadcycles", assign_int_cmd, int_base + max_dead_cycles_code, int_base); + primitive_tex("hangafter", assign_int_cmd, int_base + hang_after_code, int_base); + primitive_tex("floatingpenalty", assign_int_cmd, int_base + floating_penalty_code, int_base); + primitive_tex("globaldefs", assign_int_cmd, int_base + global_defs_code, int_base); + primitive_tex("fam", assign_int_cmd, int_base + cur_fam_code, int_base); + primitive_tex("escapechar", assign_int_cmd, int_base + escape_char_code, int_base); + primitive_tex("defaulthyphenchar", assign_int_cmd, int_base + default_hyphen_char_code, int_base); + primitive_tex("defaultskewchar", assign_int_cmd, int_base + default_skew_char_code, int_base); + primitive_tex("endlinechar", assign_int_cmd, int_base + end_line_char_code, int_base); + primitive_tex("newlinechar", assign_int_cmd, int_base + new_line_char_code, int_base); + primitive_tex("language", assign_int_cmd, int_base + language_code, int_base); + primitive_tex("setlanguage", assign_int_cmd, int_base + cur_lang_code, int_base); + primitive_tex("firstvalidlanguage", assign_int_cmd, int_base + first_valid_language_code, int_base); + primitive_tex("exhyphenchar", assign_int_cmd, int_base + ex_hyphen_char_code, int_base); + primitive_tex("lefthyphenmin", assign_int_cmd, int_base + left_hyphen_min_code, int_base); + primitive_tex("righthyphenmin", assign_int_cmd, int_base + right_hyphen_min_code, int_base); + primitive_tex("holdinginserts", assign_int_cmd, int_base + holding_inserts_code, int_base); + primitive_tex("errorcontextlines", assign_int_cmd, int_base + error_context_lines_code, int_base); + primitive_luatex("nokerns", assign_int_cmd, int_base + disable_kern_code, int_base); + primitive_luatex("noligs", assign_int_cmd, int_base + disable_lig_code, int_base); + primitive_luatex("nospaces", assign_int_cmd, int_base + disable_space_code, int_base); + primitive_luatex("catcodetable", assign_int_cmd, int_base + cat_code_table_code, int_base); + primitive_luatex("outputbox", assign_int_cmd, int_base + output_box_code, int_base); + primitive_luatex("outputmode", assign_int_cmd, int_base + output_mode_code, int_base); + primitive_luatex("adjustspacing", assign_int_cmd, int_base + adjust_spacing_code, int_base); + primitive_luatex("protrudechars", assign_int_cmd, int_base + protrude_chars_code, int_base); + primitive_luatex("tracingfonts", assign_int_cmd, int_base + tracing_fonts_code, int_base); + primitive_luatex("draftmode", assign_int_cmd, int_base + draft_mode_code, int_base); + primitive_tex("parindent", assign_dimen_cmd, dimen_base + par_indent_code, dimen_base); + primitive_tex("mathsurround", assign_dimen_cmd, dimen_base + math_surround_code, dimen_base); + primitive_tex("lineskiplimit", assign_dimen_cmd, dimen_base + line_skip_limit_code, dimen_base); + primitive_tex("hsize", assign_dimen_cmd, dimen_base + hsize_code, dimen_base); + primitive_tex("vsize", assign_dimen_cmd, dimen_base + vsize_code, dimen_base); + primitive_tex("maxdepth", assign_dimen_cmd, dimen_base + max_depth_code, dimen_base); + primitive_tex("splitmaxdepth", assign_dimen_cmd, dimen_base + split_max_depth_code, dimen_base); + primitive_tex("boxmaxdepth", assign_dimen_cmd, dimen_base + box_max_depth_code, dimen_base); + primitive_tex("hfuzz", assign_dimen_cmd, dimen_base + hfuzz_code, dimen_base); + primitive_tex("vfuzz", assign_dimen_cmd, dimen_base + vfuzz_code, dimen_base); + primitive_tex("delimitershortfall", assign_dimen_cmd, dimen_base + delimiter_shortfall_code, dimen_base); + primitive_tex("nulldelimiterspace", assign_dimen_cmd, dimen_base + null_delimiter_space_code, dimen_base); + primitive_tex("scriptspace", assign_dimen_cmd, dimen_base + script_space_code, dimen_base); + primitive_tex("predisplaysize", assign_dimen_cmd, dimen_base + pre_display_size_code, dimen_base); + primitive_tex("displaywidth", assign_dimen_cmd, dimen_base + display_width_code, dimen_base); + primitive_tex("displayindent", assign_dimen_cmd, dimen_base + display_indent_code, dimen_base); + primitive_tex("overfullrule", assign_dimen_cmd, dimen_base + overfull_rule_code, dimen_base); + primitive_tex("hangindent", assign_dimen_cmd, dimen_base + hang_indent_code, dimen_base); + primitive_tex("hoffset", assign_dimen_cmd, dimen_base + h_offset_code, dimen_base); + primitive_tex("voffset", assign_dimen_cmd, dimen_base + v_offset_code, dimen_base); + primitive_tex("emergencystretch", assign_dimen_cmd, dimen_base + emergency_stretch_code, dimen_base); + primitive_luatex("pagewidth", assign_dimen_cmd, dimen_base + page_width_code, dimen_base); + primitive_luatex("pageheight", assign_dimen_cmd, dimen_base + page_height_code, dimen_base); + primitive_luatex("pxdimen", assign_dimen_cmd, dimen_base + px_dimen_code, dimen_base); + primitive_luatex("predisplaygapfactor", assign_int_cmd, int_base + math_pre_display_gap_factor_code, int_base); + primitive_luatex("hyphenpenaltymode", assign_int_cmd, int_base + hyphen_penalty_mode_code, int_base); + primitive_luatex("automatichyphenpenalty", assign_int_cmd, int_base + automatic_hyphen_penalty_code, int_base); + primitive_luatex("explicithyphenpenalty", assign_int_cmd, int_base + explicit_hyphen_penalty_code, int_base); + + /* 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_tex(" ", ex_space_cmd, 0, 0); + primitive_tex("/", ital_corr_cmd, 0, 0); + primitive_tex("accent", accent_cmd, 0, 0); + primitive_tex("advance", advance_cmd, 0, 0); + primitive_tex("afterassignment", after_assignment_cmd, 0, 0); + primitive_tex("aftergroup", after_group_cmd, 0, 0); + primitive_tex("begingroup", begin_group_cmd, 0, 0); + primitive_tex("char", char_num_cmd, 0, 0); + primitive_tex("csname", cs_name_cmd, 0, 0); + primitive_luatex("lastnamedcs", cs_name_cmd, 1, 0); + primitive_luatex("begincsname", cs_name_cmd, 2, 0); + primitive_tex("delimiter", delim_num_cmd, 0, 0); + primitive_luatex("Udelimiter", delim_num_cmd, 1, 0); + primitive_tex("divide", divide_cmd, 0, 0); + primitive_tex("endcsname", end_cs_name_cmd, 0, 0); + primitive_tex("endgroup", end_group_cmd, 0, 0); + cs_text(frozen_end_group) = maketexstring("endgroup"); + eqtb[frozen_end_group] = eqtb[cur_val]; + primitive_tex("expandafter", expand_after_cmd, 0, 0); + primitive_tex("font", def_font_cmd, 0, 0); + primitive_luatex("letterspacefont", letterspace_font_cmd, 0, 0); + primitive_luatex("expandglyphsinfont", normal_cmd, expand_font_code, 0); + primitive_luatex("copyfont", copy_font_cmd, 0, 0); + primitive_luatex("setfontid", set_font_id_cmd, 0, 0); + primitive_tex("fontdimen", assign_font_dimen_cmd, 0, 0); + primitive_tex("halign", halign_cmd, 0, 0); + primitive_tex("hrule", hrule_cmd, 0, 0); + primitive_luatex("nohrule", no_hrule_cmd, 0, 0); + primitive_tex("ignorespaces", ignore_spaces_cmd, 0, 0); + primitive_tex("insert", insert_cmd, 0, 0); + primitive_luatex("leftghost", char_ghost_cmd, 0, 0); + primitive_tex("mark", mark_cmd, 0, 0); + primitive_tex("mathaccent", math_accent_cmd, 0, 0); + primitive_luatex("Umathaccent", math_accent_cmd, 1, 0); + primitive_tex("mathchar", math_char_num_cmd, 0, 0); + primitive_luatex("Umathchar", math_char_num_cmd, 1, 0); + primitive_luatex("Umathcharnum", math_char_num_cmd, 2, 0); + primitive_tex("mathchoice", math_choice_cmd, 0, 0); + primitive_luatex("Ustack", math_choice_cmd, 1, 0); + primitive_tex("multiply", multiply_cmd, 0, 0); + primitive_tex("noalign", no_align_cmd, 0, 0); + primitive_tex("noboundary", boundary_cmd, 0, 0); + primitive_tex("boundary", boundary_cmd, 1, 0); + primitive_tex("protrusionboundary", boundary_cmd, 2, 0); + primitive_tex("wordboundary", boundary_cmd, 3, 0); + primitive_tex("noexpand", no_expand_cmd, 0, 0); + primitive_luatex("primitive", no_expand_cmd, 1, 0); + primitive_tex("nonscript", non_script_cmd, 0, 0); + primitive_tex("omit", omit_cmd, 0, 0); + primitive_tex("parshape", set_tex_shape_cmd, par_shape_loc, par_shape_loc); + primitive_tex("penalty", break_penalty_cmd, 0, 0); + primitive_tex("prevgraf", set_prev_graf_cmd, 0, 0); + primitive_tex("radical", radical_cmd, 0, 0); + primitive_luatex("Uradical", radical_cmd, 1, 0); + primitive_luatex("Uroot", radical_cmd, 2, 0); + primitive_luatex("Uunderdelimiter", radical_cmd, 3, 0); + primitive_luatex("Uoverdelimiter", radical_cmd, 4, 0); + primitive_luatex("Udelimiterunder", radical_cmd, 5, 0); + primitive_luatex("Udelimiterover", radical_cmd, 6, 0); + primitive_luatex("Uhextensible", radical_cmd, 7, 0); + primitive_tex("read", read_to_cs_cmd, 0, 0); + primitive_tex("relax", relax_cmd, too_big_char, too_big_char); + cs_text(frozen_relax) = maketexstring("relax"); + eqtb[frozen_relax] = eqtb[cur_val]; + primitive_luatex("rightghost", char_ghost_cmd, 1, 0); + primitive_tex("setbox", set_box_cmd, 0, 0); + primitive_tex("the", the_cmd, 0, 0); + primitive_luatex("toksapp", combine_toks_cmd, 0, 0); + primitive_luatex("tokspre", combine_toks_cmd, 1, 0); + primitive_luatex("etoksapp", combine_toks_cmd, 2, 0); + primitive_luatex("etokspre", combine_toks_cmd, 3, 0); + primitive_tex("toks", toks_register_cmd, 0, 0); + primitive_tex("vadjust", vadjust_cmd, 0, 0); + primitive_tex("valign", valign_cmd, 0, 0); + primitive_tex("vcenter", vcenter_cmd, 0, 0); + primitive_tex("vrule", vrule_cmd, 0, 0); + primitive_luatex("novrule", no_vrule_cmd, 0, 0); + primitive_tex("par", par_end_cmd, too_big_char, too_big_char); /* cf.\ |scan_file_name| */ + par_loc = cur_val; + par_token = cs_token_flag + par_loc; + @; + @; + @; +} + + +@ These are in a separate module due to a CWEAVE limitation. + +@= + + /* + The processing of \.{\\input} involves the |start_input| subroutine, + which will be declared later; the processing of \.{\\endinput} is trivial. + */ + + primitive_tex("input", input_cmd, 0, 0); + primitive_tex("endinput", input_cmd, 1, 0); + primitive_tex("topmark", top_bot_mark_cmd, top_mark_code, 0); + primitive_tex("firstmark", top_bot_mark_cmd, first_mark_code, 0); + primitive_tex("botmark", top_bot_mark_cmd, bot_mark_code, 0); + primitive_tex("splitfirstmark", top_bot_mark_cmd, split_first_mark_code, 0); + primitive_tex("splitbotmark", top_bot_mark_cmd, split_bot_mark_code, 0); + primitive_luatex("clearmarks", mark_cmd, clear_marks_code, 0); + primitive_etex("marks", mark_cmd, marks_code, 0); + primitive_etex("topmarks", top_bot_mark_cmd, top_mark_code + marks_code, 0); + primitive_etex("firstmarks", top_bot_mark_cmd, first_mark_code + marks_code, 0); + primitive_etex("botmarks", top_bot_mark_cmd, bot_mark_code + marks_code, 0); + primitive_etex("splitfirstmarks", top_bot_mark_cmd, split_first_mark_code + marks_code, 0); + primitive_etex("splitbotmarks", top_bot_mark_cmd, split_bot_mark_code + marks_code, 0); + + /* + The hash table is initialized with `\.{\\count}', `\.{\\attribute}', + `\.{\\dimen}', `\.{\\skip}', and `\.{\\muskip}' all having |register| + as their command code; they are distinguished by the |chr_code|, which + is either |int_val|, |attr_val|, |dimen_val|, |glue_val|, or |mu_val|. + */ + + primitive_tex("count", register_cmd, int_val_level, 0); + primitive_luatex("attribute", register_cmd, attr_val_level, 0); + primitive_tex("dimen", register_cmd, dimen_val_level, 0); + primitive_tex("skip", register_cmd, glue_val_level, 0); + primitive_tex("muskip", register_cmd, mu_val_level, 0); + + primitive_tex("spacefactor", set_aux_cmd, hmode, 0); + primitive_tex("prevdepth", set_aux_cmd, vmode, 0); + primitive_tex("deadcycles", set_page_int_cmd, 0, 0); + primitive_tex("insertpenalties", set_page_int_cmd, 1, 0); + primitive_tex("wd", set_box_dimen_cmd, width_offset, 0); + primitive_tex("ht", set_box_dimen_cmd, height_offset, 0); + primitive_tex("dp", set_box_dimen_cmd, depth_offset, 0); + primitive_tex("lastpenalty", last_item_cmd, lastpenalty_code, 0); + primitive_tex("lastkern", last_item_cmd, lastkern_code, 0); + primitive_tex("lastskip", last_item_cmd, lastskip_code, 0); + primitive_tex("inputlineno", last_item_cmd, input_line_no_code, 0); + primitive_tex("badness", last_item_cmd, badness_code, 0); + primitive_luatex("luatexversion", last_item_cmd, luatex_version_code, 0); + primitive_luatex("lastsavedboxresourceindex", last_item_cmd, last_saved_box_resource_index_code, 0); + primitive_luatex("lastsavedimageresourceindex", last_item_cmd, last_saved_image_resource_index_code, 0); + primitive_luatex("lastsavedimageresourcepages", last_item_cmd, last_saved_image_resource_pages_code, 0); + primitive_luatex("lastxpos", last_item_cmd, last_x_pos_code, 0); + primitive_luatex("lastypos", last_item_cmd, last_y_pos_code, 0); + primitive_luatex("randomseed", last_item_cmd, random_seed_code, 0); + + primitive_tex("number", convert_cmd, number_code, 0); + primitive_tex("romannumeral", convert_cmd, roman_numeral_code, 0); + primitive_tex("string", convert_cmd, string_code, 0); + primitive_tex("csstring", convert_cmd, cs_string_code, 0); + primitive_tex("meaning", convert_cmd, meaning_code, 0); + primitive_etex("eTeXVersion", convert_cmd, etex_code, 0); + primitive_tex("fontname", convert_cmd, font_name_code, 0); + primitive_luatex("fontid", convert_cmd, font_id_code, 0); + primitive_luatex("luatexrevision", convert_cmd, luatex_revision_code, 0); + primitive_luatex("luatexdatestamp", convert_cmd, luatex_date_code, 0); + primitive_luatex("luatexbanner", convert_cmd, luatex_banner_code, 0); + primitive_luatex("leftmarginkern", convert_cmd, left_margin_kern_code, 0); + primitive_luatex("rightmarginkern", convert_cmd, right_margin_kern_code, 0); + primitive_luatex("uniformdeviate", convert_cmd, uniform_deviate_code, 0); + primitive_luatex("normaldeviate", convert_cmd, normal_deviate_code, 0); + primitive_core("directlua", convert_cmd, lua_code, 0); + primitive_luatex("luafunction", convert_cmd, lua_function_code, 0); + primitive_luatex("luaescapestring", convert_cmd, lua_escape_string_code, 0); + primitive_luatex("mathstyle", convert_cmd, math_style_code, 0); + primitive_luatex("expanded", convert_cmd, expanded_code, 0); + primitive_tex("jobname", convert_cmd, job_name_code, 0); + primitive_luatex("formatname", convert_cmd, format_name_code, 0); + primitive_luatex("Uchar", convert_cmd, uchar_code, 0); + + primitive_luatex("Umathcharclass", convert_cmd, math_char_class_code, 0); + primitive_luatex("Umathcharfam", convert_cmd, math_char_fam_code, 0); + primitive_luatex("Umathcharslot", convert_cmd, math_char_slot_code, 0); + + primitive_tex("if", if_test_cmd, if_char_code, 0); + primitive_tex("ifcat", if_test_cmd, if_cat_code, 0); + primitive_tex("ifnum", if_test_cmd, if_int_code, 0); + primitive_tex("ifdim", if_test_cmd, if_dim_code, 0); + primitive_tex("ifodd", if_test_cmd, if_odd_code, 0); + primitive_tex("ifvmode", if_test_cmd, if_vmode_code, 0); + primitive_tex("ifhmode", if_test_cmd, if_hmode_code, 0); + primitive_tex("ifmmode", if_test_cmd, if_mmode_code, 0); + primitive_tex("ifinner", if_test_cmd, if_inner_code, 0); + primitive_tex("ifvoid", if_test_cmd, if_void_code, 0); + + primitive_tex("ifhbox", if_test_cmd, if_hbox_code, 0); + primitive_tex("ifvbox", if_test_cmd, if_vbox_code, 0); + primitive_tex("ifx", if_test_cmd, ifx_code, 0); + primitive_tex("ifeof", if_test_cmd, if_eof_code, 0); + primitive_tex("iftrue", if_test_cmd, if_true_code, 0); + primitive_tex("iffalse", if_test_cmd, if_false_code, 0); + primitive_tex("ifcase", if_test_cmd, if_case_code, 0); + primitive_luatex("ifprimitive", if_test_cmd, if_primitive_code, 0); + primitive_tex("fi", fi_or_else_cmd, fi_code, 0); + cs_text(frozen_fi) = maketexstring("fi"); + eqtb[frozen_fi] = eqtb[cur_val]; + primitive_tex("or", fi_or_else_cmd, or_code, 0); + primitive_tex("else", fi_or_else_cmd, else_code, 0); + + /* + \TeX\ always knows at least one font, namely the null font. It has no + characters, and its seven parameters are all equal to zero. + */ + + primitive_tex("nullfont", set_font_cmd, null_font, 0); + cs_text(frozen_null_font) = maketexstring("nullfont"); + eqtb[frozen_null_font] = eqtb[cur_val]; + + primitive_tex("span", tab_mark_cmd, span_code, tab_mark_cmd_code); + primitive_luatex("aligntab", tab_mark_cmd, tab_mark_cmd_code, tab_mark_cmd_code); + primitive_luatex("alignmark", mac_param_cmd, tab_mark_cmd_code, tab_mark_cmd_code); + primitive_tex("cr", car_ret_cmd, cr_code, cr_code); + cs_text(frozen_cr) = maketexstring("cr"); + eqtb[frozen_cr] = eqtb[cur_val]; + primitive_tex("crcr", car_ret_cmd, cr_cr_code, cr_code); + cs_text(frozen_end_template) = maketexstring("endtemplate"); + cs_text(frozen_endv) = maketexstring("endtemplate"); + set_eq_type(frozen_endv, endv_cmd); + set_equiv(frozen_endv, null_list); + set_eq_level(frozen_endv, level_one); + eqtb[frozen_end_template] = eqtb[frozen_endv]; + set_eq_type(frozen_end_template, end_template_cmd); + + primitive_tex("pagegoal", set_page_dimen_cmd, 0, 0); + primitive_tex("pagetotal", set_page_dimen_cmd, 1, 0); + primitive_tex("pagestretch", set_page_dimen_cmd, 2, 0); + primitive_tex("pagefilstretch", set_page_dimen_cmd, 3, 0); + primitive_tex("pagefillstretch", set_page_dimen_cmd, 4, 0); + primitive_tex("pagefilllstretch", set_page_dimen_cmd, 5, 0); + primitive_tex("pageshrink", set_page_dimen_cmd, 6, 0); + primitive_tex("pagedepth", set_page_dimen_cmd, 7, 0); + + /* + Either \.{\\dump} or \.{\\end} will cause |main_control| to enter the + endgame, since both of them have `|stop|' as their command code. + */ + + primitive_tex("end", stop_cmd, 0, 0); + primitive_tex("dump", stop_cmd, 1, 0); + + primitive_tex("hskip", hskip_cmd, skip_code, 0); + primitive_tex("hfil", hskip_cmd, fil_code, 0); + primitive_tex("hfill", hskip_cmd, fill_code, 0); + primitive_tex("hss", hskip_cmd, ss_code, 0); + primitive_tex("hfilneg", hskip_cmd, fil_neg_code, 0); + primitive_tex("vskip", vskip_cmd, skip_code, 0); + primitive_tex("vfil", vskip_cmd, fil_code, 0); + primitive_tex("vfill", vskip_cmd, fill_code, 0); + primitive_tex("vss", vskip_cmd, ss_code, 0); + primitive_tex("vfilneg", vskip_cmd, fil_neg_code, 0); + primitive_tex("mskip", mskip_cmd, mskip_code, 0); + primitive_tex("kern", kern_cmd, explicit_kern, 0); + primitive_tex("mkern", mkern_cmd, mu_glue, 0); + primitive_tex("moveleft", hmove_cmd, 1, 0); + primitive_tex("moveright", hmove_cmd, 0, 0); + primitive_tex("raise", vmove_cmd, 1, 0); + primitive_tex("lower", vmove_cmd, 0, 0); + primitive_tex("box", make_box_cmd, box_code, 0); + primitive_tex("copy", make_box_cmd, copy_code, 0); + primitive_tex("lastbox", make_box_cmd, last_box_code, 0); + primitive_tex("vsplit", make_box_cmd, vsplit_code, 0); + primitive_tex("tpack", make_box_cmd, tpack_code, 0); + primitive_tex("vpack", make_box_cmd, vpack_code, 0); + primitive_tex("hpack", make_box_cmd, hpack_code, 0); + primitive_tex("vtop", make_box_cmd, vtop_code, 0); + primitive_tex("vbox", make_box_cmd, vtop_code + vmode, 0); + primitive_tex("hbox", make_box_cmd, vtop_code + hmode, 0); + primitive_tex("shipout", leader_ship_cmd, a_leaders - 1, 0); /* |ship_out_flag=leader_flag-1| */ + primitive_tex("leaders", leader_ship_cmd, a_leaders, 0); + primitive_tex("cleaders", leader_ship_cmd, c_leaders, 0); + primitive_tex("xleaders", leader_ship_cmd, x_leaders, 0); + primitive_luatex("gleaders", leader_ship_cmd, g_leaders, 0); + primitive_luatex("boxdir", assign_box_dir_cmd, 0, 0); + primitive_tex("indent", start_par_cmd, 1, 0); + primitive_tex("noindent", start_par_cmd, 0, 0); + primitive_luatex("quitvmode", start_par_cmd, 2, 0); + primitive_tex("unpenalty", remove_item_cmd, penalty_node, 0); + primitive_tex("unkern", remove_item_cmd, kern_node, 0); + primitive_tex("unskip", remove_item_cmd, glue_node, 0); + primitive_tex("unhbox", un_hbox_cmd, box_code, 0); + primitive_tex("unhcopy", un_hbox_cmd, copy_code, 0); + primitive_tex("unvbox", un_vbox_cmd, box_code, 0); + primitive_tex("unvcopy", un_vbox_cmd, copy_code, 0); + primitive_tex("-", discretionary_cmd, explicit_disc, 0); + primitive_tex("discretionary", discretionary_cmd, discretionary_disc, 0); + primitive_luatex("localleftbox", assign_local_box_cmd, 0, 0); + primitive_luatex("localrightbox", assign_local_box_cmd, 1, 0); + + primitive_luatex("Ustartmath", math_shift_cs_cmd, text_style, 0); + primitive_luatex("Ustopmath", math_shift_cs_cmd, cramped_text_style, 0); + primitive_luatex("Ustartdisplaymath", math_shift_cs_cmd, display_style, 0); + primitive_luatex("Ustopdisplaymath", math_shift_cs_cmd, cramped_display_style, 0); + primitive_tex("eqno", eq_no_cmd, 0, 0); + primitive_tex("leqno", eq_no_cmd, 1, 0); + primitive_tex("mathord", math_comp_cmd, ord_noad_type, 0); + primitive_tex("mathop", math_comp_cmd, op_noad_type_normal, 0); + primitive_tex("mathbin", math_comp_cmd, bin_noad_type, 0); + primitive_tex("mathrel", math_comp_cmd, rel_noad_type, 0); + primitive_tex("mathopen", math_comp_cmd, open_noad_type, 0); + primitive_tex("mathclose", math_comp_cmd, close_noad_type, 0); + primitive_tex("mathpunct", math_comp_cmd, punct_noad_type, 0); + primitive_tex("mathinner", math_comp_cmd, inner_noad_type, 0); + primitive_tex("underline", math_comp_cmd, under_noad_type, 0); + primitive_tex("overline", math_comp_cmd, over_noad_type, 0); + primitive_tex("displaylimits", limit_switch_cmd, op_noad_type_normal, 0); + primitive_tex("limits", limit_switch_cmd, op_noad_type_limits, 0); + primitive_tex("nolimits", limit_switch_cmd, op_noad_type_no_limits, 0); + primitive_tex("displaystyle", math_style_cmd, display_style, 0); + primitive_tex("textstyle", math_style_cmd, text_style, 0); + primitive_tex("scriptstyle", math_style_cmd, script_style, 0); + primitive_tex("scriptscriptstyle", math_style_cmd, script_script_style, 0); + primitive_luatex("crampeddisplaystyle", math_style_cmd, cramped_display_style, 0); + primitive_luatex("crampedtextstyle", math_style_cmd, cramped_text_style, 0); + primitive_luatex("crampedscriptstyle", math_style_cmd, cramped_script_style, 0); + primitive_luatex("crampedscriptscriptstyle", math_style_cmd, cramped_script_script_style, 0); + primitive_luatex("Usuperscript", super_sub_script_cmd, sup_mark_cmd, sup_mark_cmd); + primitive_luatex("Usubscript", super_sub_script_cmd, sub_mark_cmd, sup_mark_cmd); + primitive_tex("above", above_cmd, above_code, 0); + primitive_tex("over", above_cmd, over_code, 0); + primitive_tex("atop", above_cmd, atop_code, 0); + primitive_luatex("Uskewed", above_cmd, skewed_code, 0); + primitive_tex("abovewithdelims", above_cmd, delimited_code + above_code, 0); + primitive_tex("overwithdelims", above_cmd, delimited_code + over_code, 0); + primitive_tex("atopwithdelims", above_cmd, delimited_code + atop_code, 0); + primitive_luatex("Uskewedwithdelims", above_cmd, delimited_code + skewed_code, 0); + primitive_tex("left", left_right_cmd, left_noad_side, 0); + primitive_tex("right", left_right_cmd, right_noad_side, 0); + primitive_tex("middle", left_right_cmd, middle_noad_side, 0); + primitive_tex("Uleft", left_right_cmd, 10+left_noad_side, 0); + primitive_tex("Uright", left_right_cmd, 10+right_noad_side, 0); + primitive_tex("Umiddle", left_right_cmd, 10+middle_noad_side, 0); + primitive_luatex("Uvextensible", left_right_cmd, 10+no_noad_side, 0); + cs_text(frozen_right) = maketexstring("right"); + eqtb[frozen_right] = eqtb[cur_val]; + + primitive_tex("long", prefix_cmd, 1, 0); + primitive_tex("outer", prefix_cmd, 2, 0); + primitive_tex("global", prefix_cmd, 4, 0); + primitive_tex("def", def_cmd, 0, 0); + primitive_tex("gdef", def_cmd, 1, 0); + primitive_tex("edef", def_cmd, 2, 0); + primitive_tex("xdef", def_cmd, 3, 0); + primitive_tex("let", let_cmd, normal, 0); + primitive_tex("futurelet", let_cmd, normal + 1, 0); + primitive_luatex("letcharcode", let_cmd, normal + 2, 0); + primitive_tex("chardef", shorthand_def_cmd, char_def_code, 0); + primitive_tex("mathchardef", shorthand_def_cmd, math_char_def_code, 0); + primitive_luatex("Umathchardef", shorthand_def_cmd, xmath_char_def_code, 0); + primitive_luatex("Umathcharnumdef", shorthand_def_cmd, umath_char_def_code, 0); + primitive_tex("countdef", shorthand_def_cmd, count_def_code, 0); + primitive_luatex("attributedef", shorthand_def_cmd, attribute_def_code, 0); + primitive_tex("dimendef", shorthand_def_cmd, dimen_def_code, 0); + primitive_tex("skipdef", shorthand_def_cmd, skip_def_code, 0); + primitive_tex("muskipdef", shorthand_def_cmd, mu_skip_def_code, 0); + primitive_tex("toksdef", shorthand_def_cmd, toks_def_code, 0); + primitive_tex("catcode", def_char_code_cmd, cat_code_base, cat_code_base); + primitive_tex("mathcode", def_char_code_cmd, math_code_base, cat_code_base); + primitive_tex("lccode", def_char_code_cmd, lc_code_base, cat_code_base); + primitive_tex("uccode", def_char_code_cmd, uc_code_base, cat_code_base); + primitive_tex("sfcode", def_char_code_cmd, sf_code_base, cat_code_base); + primitive_tex("delcode", def_del_code_cmd, del_code_base, del_code_base); + primitive_tex("textfont", def_family_cmd, text_size, 0); + primitive_tex("scriptfont", def_family_cmd, script_size, 0); + primitive_tex("scriptscriptfont", def_family_cmd, script_script_size, 0); + primitive_luatex("Umathquad", set_math_param_cmd, math_param_quad, 0); + primitive_luatex("Umathaxis", set_math_param_cmd, math_param_axis, 0); + +@ These are in a separate module due to a CWEAVE limitation. + +@= + primitive_luatex("Umathoperatorsize", set_math_param_cmd, math_param_operator_size, 0); + primitive_luatex("Umathoverbarkern", set_math_param_cmd, math_param_overbar_kern, 0); + primitive_luatex("Umathoverbarrule", set_math_param_cmd, math_param_overbar_rule, 0); + primitive_luatex("Umathoverbarvgap", set_math_param_cmd, math_param_overbar_vgap, 0); + primitive_luatex("Umathunderbarkern", set_math_param_cmd, math_param_underbar_kern, 0); + primitive_luatex("Umathunderbarrule", set_math_param_cmd, math_param_underbar_rule, 0); + primitive_luatex("Umathunderbarvgap", set_math_param_cmd, math_param_underbar_vgap, 0); + primitive_luatex("Umathradicalkern", set_math_param_cmd, math_param_radical_kern, 0); + primitive_luatex("Umathradicalrule", set_math_param_cmd, math_param_radical_rule, 0); + primitive_luatex("Umathradicalvgap", set_math_param_cmd, math_param_radical_vgap, 0); + primitive_luatex("Umathradicaldegreebefore", set_math_param_cmd, math_param_radical_degree_before, 0); + primitive_luatex("Umathradicaldegreeafter", set_math_param_cmd, math_param_radical_degree_after, 0); + primitive_luatex("Umathradicaldegreeraise", set_math_param_cmd, math_param_radical_degree_raise, 0); + primitive_luatex("Umathstackvgap", set_math_param_cmd, math_param_stack_vgap, 0); + primitive_luatex("Umathstacknumup", set_math_param_cmd, math_param_stack_num_up, 0); + primitive_luatex("Umathstackdenomdown", set_math_param_cmd, math_param_stack_denom_down, 0); + primitive_luatex("Umathfractionrule", set_math_param_cmd, math_param_fraction_rule, 0); + primitive_luatex("Umathfractionnumvgap", set_math_param_cmd, math_param_fraction_num_vgap, 0); + primitive_luatex("Umathfractionnumup", set_math_param_cmd, math_param_fraction_num_up, 0); + primitive_luatex("Umathfractiondenomvgap", set_math_param_cmd, math_param_fraction_denom_vgap, 0); + primitive_luatex("Umathfractiondenomdown", set_math_param_cmd, math_param_fraction_denom_down, 0); + primitive_luatex("Umathfractiondelsize", set_math_param_cmd, math_param_fraction_del_size, 0); + primitive_luatex("Umathskewedfractionvgap", set_math_param_cmd, math_param_skewed_fraction_vgap, 0); + primitive_luatex("Umathskewedfractionhgap", set_math_param_cmd, math_param_skewed_fraction_hgap, 0); + primitive_luatex("Umathlimitabovevgap", set_math_param_cmd, math_param_limit_above_vgap, 0); + primitive_luatex("Umathlimitabovebgap", set_math_param_cmd, math_param_limit_above_bgap, 0); + primitive_luatex("Umathlimitabovekern", set_math_param_cmd, math_param_limit_above_kern, 0); + primitive_luatex("Umathlimitbelowvgap", set_math_param_cmd, math_param_limit_below_vgap, 0); + primitive_luatex("Umathlimitbelowbgap", set_math_param_cmd, math_param_limit_below_bgap, 0); + primitive_luatex("Umathlimitbelowkern", set_math_param_cmd, math_param_limit_below_kern, 0); + primitive_luatex("Umathnolimitsubfactor", set_math_param_cmd, math_param_nolimit_sub_factor, 0); /* bonus */ + primitive_luatex("Umathnolimitsupfactor", set_math_param_cmd, math_param_nolimit_sup_factor, 0); /* bonus */ + primitive_luatex("Umathunderdelimitervgap", set_math_param_cmd, math_param_under_delimiter_vgap, 0); + primitive_luatex("Umathunderdelimiterbgap", set_math_param_cmd, math_param_under_delimiter_bgap, 0); + primitive_luatex("Umathoverdelimitervgap", set_math_param_cmd, math_param_over_delimiter_vgap, 0); + primitive_luatex("Umathoverdelimiterbgap", set_math_param_cmd, math_param_over_delimiter_bgap, 0); + primitive_luatex("Umathsubshiftdrop", set_math_param_cmd, math_param_sub_shift_drop, 0); + primitive_luatex("Umathsupshiftdrop", set_math_param_cmd, math_param_sup_shift_drop, 0); + primitive_luatex("Umathsubshiftdown", set_math_param_cmd, math_param_sub_shift_down, 0); + primitive_luatex("Umathsubsupshiftdown", set_math_param_cmd, math_param_sub_sup_shift_down, 0); + primitive_luatex("Umathsubtopmax", set_math_param_cmd, math_param_sub_top_max, 0); + primitive_luatex("Umathsupshiftup", set_math_param_cmd, math_param_sup_shift_up, 0); + primitive_luatex("Umathsupbottommin", set_math_param_cmd, math_param_sup_bottom_min, 0); + primitive_luatex("Umathsupsubbottommax", set_math_param_cmd, math_param_sup_sub_bottom_max, 0); + primitive_luatex("Umathsubsupvgap", set_math_param_cmd, math_param_subsup_vgap, 0); + primitive_luatex("Umathspaceafterscript", set_math_param_cmd, math_param_space_after_script, 0); + primitive_luatex("Umathconnectoroverlapmin", set_math_param_cmd, math_param_connector_overlap_min, 0); + primitive_luatex("Umathordordspacing", set_math_param_cmd, math_param_ord_ord_spacing, 0); + primitive_luatex("Umathordopspacing", set_math_param_cmd, math_param_ord_op_spacing, 0); + primitive_luatex("Umathordbinspacing", set_math_param_cmd, math_param_ord_bin_spacing, 0); + primitive_luatex("Umathordrelspacing", set_math_param_cmd, math_param_ord_rel_spacing, 0); + primitive_luatex("Umathordopenspacing", set_math_param_cmd, math_param_ord_open_spacing, 0); + primitive_luatex("Umathordclosespacing", set_math_param_cmd, math_param_ord_close_spacing, 0); + primitive_luatex("Umathordpunctspacing", set_math_param_cmd, math_param_ord_punct_spacing, 0); + primitive_luatex("Umathordinnerspacing", set_math_param_cmd, math_param_ord_inner_spacing, 0); + primitive_luatex("Umathopordspacing", set_math_param_cmd, math_param_op_ord_spacing, 0); + primitive_luatex("Umathopopspacing", set_math_param_cmd, math_param_op_op_spacing, 0); + primitive_luatex("Umathopbinspacing", set_math_param_cmd, math_param_op_bin_spacing, 0); + primitive_luatex("Umathoprelspacing", set_math_param_cmd, math_param_op_rel_spacing, 0); + primitive_luatex("Umathopopenspacing", set_math_param_cmd, math_param_op_open_spacing, 0); + primitive_luatex("Umathopclosespacing", set_math_param_cmd, math_param_op_close_spacing, 0); + primitive_luatex("Umathoppunctspacing", set_math_param_cmd, math_param_op_punct_spacing, 0); + primitive_luatex("Umathopinnerspacing", set_math_param_cmd, math_param_op_inner_spacing, 0); + primitive_luatex("Umathbinordspacing", set_math_param_cmd, math_param_bin_ord_spacing, 0); + primitive_luatex("Umathbinopspacing", set_math_param_cmd, math_param_bin_op_spacing, 0); + primitive_luatex("Umathbinbinspacing", set_math_param_cmd, math_param_bin_bin_spacing, 0); + primitive_luatex("Umathbinrelspacing", set_math_param_cmd, math_param_bin_rel_spacing, 0); + primitive_luatex("Umathbinopenspacing", set_math_param_cmd, math_param_bin_open_spacing, 0); + primitive_luatex("Umathbinclosespacing", set_math_param_cmd, math_param_bin_close_spacing, 0); + primitive_luatex("Umathbinpunctspacing", set_math_param_cmd, math_param_bin_punct_spacing, 0); + primitive_luatex("Umathbininnerspacing", set_math_param_cmd, math_param_bin_inner_spacing, 0); + primitive_luatex("Umathrelordspacing", set_math_param_cmd, math_param_rel_ord_spacing, 0); + primitive_luatex("Umathrelopspacing", set_math_param_cmd, math_param_rel_op_spacing, 0); + primitive_luatex("Umathrelbinspacing", set_math_param_cmd, math_param_rel_bin_spacing, 0); + primitive_luatex("Umathrelrelspacing", set_math_param_cmd, math_param_rel_rel_spacing, 0); + primitive_luatex("Umathrelopenspacing", set_math_param_cmd, math_param_rel_open_spacing, 0); + primitive_luatex("Umathrelclosespacing", set_math_param_cmd, math_param_rel_close_spacing, 0); + primitive_luatex("Umathrelpunctspacing", set_math_param_cmd, math_param_rel_punct_spacing, 0); + primitive_luatex("Umathrelinnerspacing", set_math_param_cmd, math_param_rel_inner_spacing, 0); + primitive_luatex("Umathopenordspacing", set_math_param_cmd, math_param_open_ord_spacing, 0); + primitive_luatex("Umathopenopspacing", set_math_param_cmd, math_param_open_op_spacing, 0); + primitive_luatex("Umathopenbinspacing", set_math_param_cmd, math_param_open_bin_spacing, 0); + primitive_luatex("Umathopenrelspacing", set_math_param_cmd, math_param_open_rel_spacing, 0); + primitive_luatex("Umathopenopenspacing", set_math_param_cmd, math_param_open_open_spacing, 0); + primitive_luatex("Umathopenclosespacing", set_math_param_cmd, math_param_open_close_spacing, 0); + primitive_luatex("Umathopenpunctspacing", set_math_param_cmd, math_param_open_punct_spacing, 0); + primitive_luatex("Umathopeninnerspacing", set_math_param_cmd, math_param_open_inner_spacing, 0); + primitive_luatex("Umathcloseordspacing", set_math_param_cmd, math_param_close_ord_spacing, 0); + primitive_luatex("Umathcloseopspacing", set_math_param_cmd, math_param_close_op_spacing, 0); + primitive_luatex("Umathclosebinspacing", set_math_param_cmd, math_param_close_bin_spacing, 0); + primitive_luatex("Umathcloserelspacing", set_math_param_cmd, math_param_close_rel_spacing, 0); + primitive_luatex("Umathcloseopenspacing", set_math_param_cmd, math_param_close_open_spacing, 0); + primitive_luatex("Umathcloseclosespacing", set_math_param_cmd, math_param_close_close_spacing, 0); + primitive_luatex("Umathclosepunctspacing", set_math_param_cmd, math_param_close_punct_spacing, 0); + primitive_luatex("Umathcloseinnerspacing", set_math_param_cmd, math_param_close_inner_spacing, 0); + primitive_luatex("Umathpunctordspacing", set_math_param_cmd, math_param_punct_ord_spacing, 0); + primitive_luatex("Umathpunctopspacing", set_math_param_cmd, math_param_punct_op_spacing, 0); + primitive_luatex("Umathpunctbinspacing", set_math_param_cmd, math_param_punct_bin_spacing, 0); + primitive_luatex("Umathpunctrelspacing", set_math_param_cmd, math_param_punct_rel_spacing, 0); + primitive_luatex("Umathpunctopenspacing", set_math_param_cmd, math_param_punct_open_spacing, 0); + primitive_luatex("Umathpunctclosespacing", set_math_param_cmd, math_param_punct_close_spacing, 0); + primitive_luatex("Umathpunctpunctspacing", set_math_param_cmd, math_param_punct_punct_spacing, 0); + primitive_luatex("Umathpunctinnerspacing", set_math_param_cmd, math_param_punct_inner_spacing, 0); + primitive_luatex("Umathinnerordspacing", set_math_param_cmd, math_param_inner_ord_spacing, 0); + primitive_luatex("Umathinneropspacing", set_math_param_cmd, math_param_inner_op_spacing, 0); + primitive_luatex("Umathinnerbinspacing", set_math_param_cmd, math_param_inner_bin_spacing, 0); + primitive_luatex("Umathinnerrelspacing", set_math_param_cmd, math_param_inner_rel_spacing, 0); + primitive_luatex("Umathinneropenspacing", set_math_param_cmd, math_param_inner_open_spacing, 0); + primitive_luatex("Umathinnerclosespacing", set_math_param_cmd, math_param_inner_close_spacing, 0); + primitive_luatex("Umathinnerpunctspacing", set_math_param_cmd, math_param_inner_punct_spacing, 0); + primitive_luatex("Umathinnerinnerspacing", set_math_param_cmd, math_param_inner_inner_spacing, 0); + +@ These are in a separate module due to a CWEAVE limitation. + +@= + primitive_luatex("Umathcode", extdef_math_code_cmd, math_code_base, math_code_base); + primitive_luatex("Udelcode", extdef_del_code_cmd, del_code_base, del_code_base); + primitive_luatex("Umathcodenum", extdef_math_code_cmd, math_code_base + 1, math_code_base); + primitive_luatex("Udelcodenum", extdef_del_code_cmd, del_code_base + 1, del_code_base); + primitive_tex("hyphenation", hyph_data_cmd, 0, 0); + primitive_tex("patterns", hyph_data_cmd, 1, 0); + primitive_luatex("prehyphenchar", hyph_data_cmd, 2, 0); + primitive_luatex("posthyphenchar", hyph_data_cmd, 3, 0); + primitive_luatex("preexhyphenchar", hyph_data_cmd, 4, 0); + primitive_luatex("postexhyphenchar", hyph_data_cmd, 5, 0); + primitive_luatex("hyphenationmin", hyph_data_cmd, 6, 0); + primitive_luatex("hjcode", hyph_data_cmd, 7, 0); + primitive_tex("hyphenchar", assign_font_int_cmd, 0, 0); + primitive_tex("skewchar", assign_font_int_cmd, 1, 0); + primitive_luatex("lpcode", assign_font_int_cmd, lp_code_base, 0); + primitive_luatex("rpcode", assign_font_int_cmd, rp_code_base, 0); + primitive_luatex("efcode", assign_font_int_cmd, ef_code_base, 0); + primitive_luatex("tagcode", assign_font_int_cmd, tag_code, 0); + primitive_luatex("ignoreligaturesinfont", assign_font_int_cmd, no_lig_code, 0); + primitive_tex("batchmode", set_interaction_cmd, batch_mode, 0); + primitive_tex("nonstopmode", set_interaction_cmd, nonstop_mode, 0); + primitive_tex("scrollmode", set_interaction_cmd, scroll_mode, 0); + primitive_tex("errorstopmode", set_interaction_cmd, error_stop_mode, 0); + primitive_tex("openin", in_stream_cmd, 1, 0); + primitive_tex("closein", in_stream_cmd, 0, 0); + primitive_tex("message", message_cmd, 0, 0); + primitive_tex("errmessage", message_cmd, 1, 0); + primitive_tex("lowercase", case_shift_cmd, lc_code_base, lc_code_base); + primitive_tex("uppercase", case_shift_cmd, uc_code_base, lc_code_base); + primitive_tex("show", xray_cmd, show_code, 0); + primitive_tex("showbox", xray_cmd, show_box_code, 0); + primitive_tex("showthe", xray_cmd, show_the_code, 0); + primitive_tex("showlists", xray_cmd, show_lists, 0); + + primitive_tex("openout", extension_cmd, open_code, 0); + primitive_tex("write", extension_cmd, write_code, 0); + write_loc = cur_val; + primitive_tex("closeout", extension_cmd, close_code, 0); + primitive_tex("special", extension_cmd, special_code, 0); + cs_text(frozen_special) = maketexstring("special"); + eqtb[frozen_special] = eqtb[cur_val]; + primitive_tex("immediate", extension_cmd, immediate_code, 0); + primitive_luatex("localinterlinepenalty", assign_int_cmd, int_base + local_inter_line_penalty_code, int_base); + primitive_luatex("localbrokenpenalty", assign_int_cmd, int_base + local_broken_penalty_code, int_base); + primitive_luatex("pagedir", assign_dir_cmd, int_base + page_direction_code, dir_base); + primitive_luatex("bodydir", assign_dir_cmd, int_base + body_direction_code, dir_base); + primitive_luatex("pardir", assign_dir_cmd, int_base + par_direction_code, dir_base); + primitive_luatex("textdir", assign_dir_cmd, int_base + text_direction_code, dir_base); + primitive_luatex("mathdir", assign_dir_cmd, int_base + math_direction_code, dir_base); + primitive_luatex("linedir", assign_dir_cmd, int_base + line_direction_code, dir_base); + primitive_luatex("pageleftoffset", assign_dimen_cmd, dimen_base + page_left_offset_code, dimen_base); + primitive_luatex("pagetopoffset", assign_dimen_cmd, dimen_base + page_top_offset_code, dimen_base); + primitive_luatex("pagerightoffset", assign_dimen_cmd, dimen_base + page_right_offset_code, dimen_base); + primitive_luatex("pagebottomoffset", assign_dimen_cmd, dimen_base + page_bottom_offset_code, dimen_base); + primitive_luatex("saveboxresource", extension_cmd, save_box_resource_code, 0); + primitive_luatex("useboxresource", extension_cmd, use_box_resource_code, 0); + primitive_luatex("saveimageresource", extension_cmd, save_image_resource_code, 0); + primitive_luatex("useimageresource", extension_cmd, use_image_resource_code, 0); + primitive_luatex("savepos", normal_cmd, save_pos_code, 0); + primitive_luatex("savecatcodetable", normal_cmd, save_cat_code_table_code, 0); + primitive_luatex("initcatcodetable", normal_cmd, init_cat_code_table_code, 0); + primitive_luatex("setrandomseed", normal_cmd, set_random_seed_code, 0); + primitive_luatex("latelua", normal_cmd, late_lua_code, 0); + primitive_luatex("insertht", convert_cmd, insert_ht_code, 0); + primitive_luatex("dviextension", extension_cmd, dvi_extension_code, 0); + primitive_luatex("dvifeedback", feedback_cmd, dvi_feedback_code, 0); + primitive_luatex("dvivariable", variable_cmd, dvi_variable_code, 0); + primitive_luatex("pdfextension", extension_cmd, pdf_extension_code, 0); + primitive_luatex("pdffeedback", feedback_cmd, pdf_feedback_code, 0); + primitive_luatex("pdfvariable", variable_cmd, pdf_variable_code, 0); + primitive_luatex("mathoption", option_cmd, math_option_code, 0); + + /* + some of the internal integer parameters are not associated with actual + primitives at all. + */ + + primitive_no("nolocalwhatsits", assign_int_cmd, int_base + no_local_whatsits_code, int_base); + primitive_no("nolocaldirs", assign_int_cmd, int_base + no_local_dirs_code, int_base); + + +@ @c +void initialize_etex_commands(void) +{ + primitive_etex("lastnodetype", last_item_cmd, last_node_type_code, 0); + primitive_etex("eTeXversion", last_item_cmd, eTeX_version_code, 0); + primitive_etex("eTeXminorversion", last_item_cmd, eTeX_minor_version_code, 0); + primitive_etex("eTeXrevision", convert_cmd, eTeX_revision_code, 0); + + /* + First we implement the additional \eTeX\ parameters in the table of equivalents. + */ + + primitive_etex("everyeof", assign_toks_cmd, every_eof_loc, local_base); + primitive_etex("tracingassigns", assign_int_cmd, int_base + tracing_assigns_code, int_base); + primitive_etex("tracinggroups", assign_int_cmd, int_base + tracing_groups_code, int_base); + primitive_etex("tracingifs", assign_int_cmd, int_base + tracing_ifs_code, int_base); + primitive_etex("tracingscantokens", assign_int_cmd, int_base + tracing_scan_tokens_code, int_base); + primitive_etex("tracingnesting", assign_int_cmd, int_base + tracing_nesting_code, int_base); + primitive_etex("predisplaydirection", assign_int_cmd, int_base + pre_display_direction_code, int_base); + primitive_etex("lastlinefit", assign_int_cmd, int_base + last_line_fit_code, int_base); + primitive_etex("savingvdiscards", assign_int_cmd, int_base + saving_vdiscards_code, int_base); + primitive_etex("savinghyphcodes", assign_int_cmd, int_base + saving_hyph_codes_code, int_base); + primitive_luatex("suppressfontnotfounderror", assign_int_cmd, int_base + suppress_fontnotfound_error_code, int_base); + primitive_luatex("suppresslongerror", assign_int_cmd, int_base + suppress_long_error_code, int_base); + primitive_luatex("suppressmathparerror", assign_int_cmd, int_base + suppress_mathpar_error_code, int_base); + primitive_luatex("suppressifcsnameerror", assign_int_cmd, int_base + suppress_ifcsname_error_code, int_base); + primitive_luatex("suppressoutererror", assign_int_cmd, int_base + suppress_outer_error_code, int_base); + primitive_luatex("matheqnogapstep", assign_int_cmd, int_base + math_eqno_gap_step_code, int_base); + primitive_luatex("mathdisplayskipmode", assign_int_cmd, int_base + math_display_skip_mode_code, int_base); + primitive_luatex("mathscriptsmode", assign_int_cmd, int_base + math_scripts_mode_code, int_base); + primitive_luatex("mathnolimitsmode", assign_int_cmd, int_base + math_nolimits_mode_code, int_base); + primitive_luatex("mathitalicsmode", assign_int_cmd, int_base + math_italics_mode_code, int_base); + primitive_luatex("mathrulesmode", assign_int_cmd, int_base + math_rules_mode_code, int_base); + primitive_luatex("mathrulesfam", assign_int_cmd, int_base + math_rules_fam_code, int_base); + primitive_luatex("synctex", assign_int_cmd, int_base + synctex_code, int_base); + + primitive_etex("currentgrouplevel", last_item_cmd, current_group_level_code, 0); + primitive_etex("currentgrouptype", last_item_cmd, current_group_type_code, 0); + + primitive_etex("currentiflevel", last_item_cmd, current_if_level_code, 0); + primitive_etex("currentiftype", last_item_cmd, current_if_type_code, 0); + primitive_etex("currentifbranch", last_item_cmd, current_if_branch_code, 0); + primitive_etex("fontcharwd", last_item_cmd, font_char_wd_code, 0); + primitive_etex("fontcharht", last_item_cmd, font_char_ht_code, 0); + primitive_etex("fontchardp", last_item_cmd, font_char_dp_code, 0); + primitive_etex("fontcharic", last_item_cmd, font_char_ic_code, 0); + + primitive_etex("parshapelength", last_item_cmd, par_shape_length_code, 0); + primitive_etex("parshapeindent", last_item_cmd, par_shape_indent_code, 0); + primitive_etex("parshapedimen", last_item_cmd, par_shape_dimen_code, 0); + + primitive_luatex("shapemode", assign_int_cmd, int_base + shape_mode_code, int_base); + primitive_luatex("hyphenationbounds", assign_int_cmd, int_base + hyphenation_bounds_code, int_base); + + primitive_etex("showgroups", xray_cmd, show_groups, 0); + + /* + The \.{\\showtokens} command displays a token list. + */ + + primitive_etex("showtokens", xray_cmd, show_tokens, 0); + + /* + The \.{\\unexpanded} primitive prevents expansion of tokens much as + the result from \.{\\the} applied to a token variable. The + \.{\\detokenize} primitive converts a token list into a list of + character tokens much as if the token list were written to a file. We + use the fact that the command modifiers for \.{\\unexpanded} and + \.{\\detokenize} are odd whereas those for \.{\\the} and \.{\\showthe} + are even. + */ + + primitive_etex("unexpanded", the_cmd, 1, 0); + primitive_etex("detokenize", the_cmd, show_tokens, 0); + + /* + The \.{\\showifs} command displays all currently active conditionals. + */ + + primitive_etex("showifs", xray_cmd, show_ifs, 0); + + /* + The \.{\\interactionmode} primitive allows to query and set the interaction mode. + */ + + primitive_etex("interactionmode", set_page_int_cmd, 2, 0); + + /* + The |scan_tokens| feature of \eTeX\ defines the \.{\\scantokens} primitive. + */ + + primitive_etex("scantokens", input_cmd, 2, 0); + primitive_luatex("scantextokens", input_cmd, 3, 0); + + primitive_etex("readline", read_to_cs_cmd, 1, 0); + + primitive_etex("unless", expand_after_cmd, 1, 0); + primitive_etex("ifdefined", if_test_cmd, if_def_code, 0); + primitive_etex("ifcsname", if_test_cmd, if_cs_code, 0); + primitive_etex("iffontchar", if_test_cmd, if_font_char_code, 0); + primitive_luatex("ifincsname", if_test_cmd, if_in_csname_code, 0); + primitive_luatex("ifabsnum", if_test_cmd, if_abs_num_code, 0); + primitive_luatex("ifabsdim", if_test_cmd, if_abs_dim_code, 0); + + /* + The |protected| feature of \eTeX\ defines the \.{\\protected} prefix + command for macro definitions. Such macros are protected against + expansions when lists of expanded tokens are built, e.g., for \.{\\edef} + or during \.{\\write}. + */ + + primitive_etex("protected", prefix_cmd, 8, 0); + + /* + Here are the additional \eTeX\ primitives for expressions. + */ + + primitive_etex("numexpr", last_item_cmd, eTeX_expr - int_val_level + int_val_level, 0); + primitive_etex("dimexpr", last_item_cmd, eTeX_expr - int_val_level + dimen_val_level, 0); + primitive_etex("glueexpr", last_item_cmd, eTeX_expr - int_val_level + glue_val_level, 0); + primitive_etex("muexpr", last_item_cmd, eTeX_expr - int_val_level + mu_val_level, 0); + + primitive_etex("gluestretchorder", last_item_cmd, glue_stretch_order_code, 0); + primitive_etex("glueshrinkorder", last_item_cmd, glue_shrink_order_code, 0); + primitive_etex("gluestretch", last_item_cmd, glue_stretch_code, 0); + primitive_etex("glueshrink", last_item_cmd, glue_shrink_code, 0); + + primitive_etex("mutoglue", last_item_cmd, mu_to_glue_code, 0); + primitive_etex("gluetomu", last_item_cmd, glue_to_mu_code, 0); + + /* + The \.{\\pagediscards} and \.{\\splitdiscards} commands share the + command code |un_vbox| with \.{\\unvbox} and \.{\\unvcopy}, they are + distinguished by their |chr_code| values |last_box_code| and + |vsplit_code|. These |chr_code| values are larger than |box_code| and + |copy_code|. + */ + + primitive_etex("pagediscards", un_vbox_cmd, last_box_code, 0); + primitive_etex("splitdiscards", un_vbox_cmd, vsplit_code, 0); + + /* + The \.{\\interlinepenalties}, \.{\\clubpenalties}, \.{\\widowpenalties}, + and \.{\\displaywidowpenalties} commands allow to define arrays of + penalty values to be used instead of the corresponding single values. + */ + + primitive_etex("interlinepenalties", set_etex_shape_cmd, inter_line_penalties_loc, etex_pen_base); + primitive_etex("clubpenalties", set_etex_shape_cmd, club_penalties_loc, etex_pen_base); + primitive_etex("widowpenalties", set_etex_shape_cmd, widow_penalties_loc, etex_pen_base); + primitive_etex("displaywidowpenalties", set_etex_shape_cmd, display_widow_penalties_loc, etex_pen_base); + +} diff --git a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w b/Build/source/texk/web2c/luatexdir/tex/dumpdata.w index 8f1261fb426..0c4e51a7593 100644 --- a/Build/source/texk/web2c/luatexdir/tex/dumpdata.w +++ b/Build/source/texk/web2c/luatexdir/tex/dumpdata.w @@ -1,526 +1,526 @@ -% dumpdata.w -% -% Copyright 2009-2010 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c - -#include "ptexlib.h" - -/* we start with 907: the sum of the values of the bytes of "don knuth" */ - -#define FORMAT_ID (907+25) -#if ((FORMAT_ID>=0) && (FORMAT_ID<=256)) -#error Wrong value for FORMAT_ID. -#endif - - -@ After \.{INITEX} has seen a collection of fonts and macros, it -can write all the necessary information on an auxiliary file so -that production versions of \TeX\ are able to initialize their -memory at high speed. The present section of the program takes -care of such output and input. We shall consider simultaneously -the processes of storing and restoring, -so that the inverse relation between them is clear. -@.INITEX@> - -The global variable |format_ident| is a string that is printed right -after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this -string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says, -for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year, -month, and day that the format file was created. We have |format_ident=0| -before \TeX's tables are loaded. |FORMAT_ID| is a new field of type int -suitable for the identification of a format: values between 0 and 256 -(included) can not be used because in the previous format they are used -for the length of the name of the engine. -@c -str_number format_ident; -str_number format_name; /* principal file name */ - - -@ Format files consist of |memory_word| items, and we use the following -macros to dump words of different types: - -@c -FILE *fmt_file; /* for input or output of format information */ - -@ @c -void store_fmt_file(void) -{ - int j, k, l; /* all-purpose indices */ - halfword p; /* all-purpose pointer */ - int x; /* something to dump */ - char *format_engine; - int callback_id; /* |pre_dump| callback */ - char *fmtname = NULL; - /* If dumping is not allowed, abort */ - /* The user is not allowed to dump a format file unless |save_ptr=0|. - This condition implies that |cur_level=level_one|, hence - the |xeq_level| array is constant and it need not be dumped. */ - if (save_ptr != 0) { - print_err("You can't dump inside a group"); - help1("`{...\\dump}' is a no-no."); - succumb(); - } - - /* Create the |format_ident|, open the format file, and inform the user - that dumping has begun */ - callback_id = callback_defined(pre_dump_callback); - if (callback_id > 0) { - (void) run_callback(callback_id, "->"); - } - selector = new_string; - tprint(" (format="); - print(job_name); - print_char(' '); - print_int(year_par); - print_char('.'); - print_int(month_par); - print_char('.'); - print_int(day_par); - print_char(')'); - str_room(2); - format_ident = make_string(); - print(job_name); - format_name = make_string(); - if (interaction == batch_mode) - selector = log_only; - else - selector = term_and_log; - - fmtname = pack_job_name(format_extension); - while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) { - fmtname = prompt_file_name("format file name", format_extension); - } - tprint_nl("Beginning to dump on file "); - tprint(fmtname); - free(fmtname); - tprint_nl(""); - print(format_ident); - - /* Dump constants for consistency check */ - /* The next few sections of the program should make it clear how we use the - dump/undump macros. */ - - dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */ - dump_int(FORMAT_ID); - - /* Align engine to 4 bytes with one or more trailing NUL */ - x = (int) strlen(engine_name); - format_engine = xmalloc((unsigned) (x + 4)); - strcpy(format_engine, engine_name); - for (k = x; k <= x + 3; k++) - format_engine[k] = 0; - x = x + 4 - (x % 4); - dump_int(x); - dump_things(format_engine[0], x); - xfree(format_engine); - dump_int(0x57325458); /* TODO HM, what checksum would make sense? */ - dump_int(max_halfword); - dump_int(hash_high); - dump_int(eqtb_size); - dump_int(hash_prime); - - /* Dump the string pool */ - k = dump_string_pool(); - print_ln(); - print_int(k); - tprint(" strings using "); - print_int((longinteger) pool_size); - tprint(" bytes"); - - /* Dump the dynamic memory */ - /* By sorting the list of available spaces in the variable-size portion of - |mem|, we are usually able to get by without having to dump very much - of the dynamic memory. - - We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid - information even when it has not been gathering statistics. - */ - dump_node_mem(); - dump_int(temp_token_head); - dump_int(hold_token_head); - dump_int(omit_template); - dump_int(null_list); - dump_int(backup_head); - dump_int(garbage); - x = (int) fix_mem_min; - dump_int(x); - x = (int) fix_mem_max; - dump_int(x); - x = (int) fix_mem_end; - dump_int(x); - dump_int(avail); - dyn_used = (int) fix_mem_end + 1; - dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); - x = x + (int) (fix_mem_end + 1 - fix_mem_min); - p = avail; - while (p != null) { - decr(dyn_used); - p = token_link(p); - } - dump_int(dyn_used); - print_ln(); - print_int(x); - tprint(" memory locations dumped; current usage is "); - print_int(var_used); - print_char('&'); - print_int(dyn_used); - - /* Dump the table of equivalents */ - /* Dump regions 1 to 4 of |eqtb| */ - /*The table of equivalents usually contains repeated information, so we dump it - in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the - format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra - copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. - */ - k = null_cs; - do { - j = k; - while (j < int_base - 1) { - if ((equiv(j) == equiv(j + 1)) && (eq_type(j) == eq_type(j + 1)) && - (eq_level(j) == eq_level(j + 1))) - goto FOUND1; - incr(j); - } - l = int_base; - goto DONE1; /* |j=int_base-1| */ - FOUND1: - incr(j); - l = j; - while (j < int_base - 1) { - if ((equiv(j) != equiv(j + 1)) || (eq_type(j) != eq_type(j + 1)) || - (eq_level(j) != eq_level(j + 1))) - goto DONE1; - incr(j); - } - DONE1: - dump_int(l - k); - dump_things(eqtb[k], l - k); - k = j + 1; - dump_int(k - l); - } while (k != int_base); - - /* Dump regions 5 and 6 of |eqtb| */ - do { - j = k; - while (j < eqtb_size) { - if (eqtb[j].cint == eqtb[j + 1].cint) - goto FOUND2; - incr(j); - } - l = eqtb_size + 1; - goto DONE2; /* |j=eqtb_size| */ - FOUND2: - incr(j); - l = j; - while (j < eqtb_size) { - if (eqtb[j].cint != eqtb[j + 1].cint) - goto DONE2; - incr(j); - } - DONE2: - dump_int(l - k); - dump_things(eqtb[k], l - k); - k = j + 1; - dump_int(k - l); - } while (k <= eqtb_size); - if (hash_high > 0) - dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */ - - dump_int(par_loc); - dump_int(write_loc); - dump_math_codes(); - dump_text_codes(); - /* Dump the hash table */ - /* A different scheme is used to compress the hash table, since its lower - region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output - two words, |p| and |hash[p]|. The hash table is, of course, densely packed - for |p>=hash_used|, so the remaining entries are output in a~block. - */ - dump_primitives(); - dump_int(hash_used); - cs_count = frozen_control_sequence - 1 - hash_used + hash_high; - for (p = hash_base; p <= hash_used; p++) { - if (cs_text(p) != 0) { - dump_int(p); - dump_hh(hash[p]); - incr(cs_count); - } - } - dump_things(hash[hash_used + 1], - undefined_control_sequence - 1 - hash_used); - if (hash_high > 0) - dump_things(hash[eqtb_size + 1], hash_high); - dump_int(cs_count); - print_ln(); - print_int(cs_count); - tprint(" multiletter control sequences"); - - /* Dump the font information */ - dump_int(max_font_id()); - for (k = 0; k <= max_font_id(); k++) { - /* Dump the array info for internal font number |k| */ - dump_font(k); - tprint_nl("\\font"); - print_esc(font_id_text(k)); - print_char('='); - tprint_file_name((unsigned char *) font_name(k), - (unsigned char *) font_area(k), NULL); - if (font_size(k) != font_dsize(k)) { - tprint(" at "); - print_scaled(font_size(k)); - tprint("pt"); - } - } - print_ln(); - print_int(max_font_id()); - tprint(" preloaded font"); - if (max_font_id() != 1) - print_char('s'); - dump_math_data(); - - /* Dump the hyphenation tables */ - dump_language_data(); - - /* Dump a couple more things and the closing check word */ - dump_int(interaction); - dump_int(format_ident); - dump_int(format_name); - dump_int(69069); - /* We have already printed a lot of statistics, so we set |tracing_stats:=0| - to prevent them from appearing again. */ - tracing_stats_par = 0; - - /* Dump the lua bytecodes */ - dump_luac_registers(); - - /* Close the format file */ - zwclose(fmt_file); -} - -@ Corresponding to the procedure that dumps a format file, we have a function -that reads one in. The function returns |false| if the dumped format is -incompatible with the present \TeX\ table sizes, etc. - -@c -#define too_small(A) do { \ - wake_up_terminal(); \ - wterm_cr(); \ - fprintf(term_out,"---! Must increase the %s",(A)); \ - goto BAD_FMT; \ - } while (0) - -@ The inverse macros are slightly more complicated, since we need to check -the range of the values we are reading in. We say `|undump(a)(b)(x)|' to -read an integer value |x| that is supposed to be in the range |a<=x<=b|. - -@c -#define undump(A,B,C) do { \ - undump_int(x); \ - if (x<(A) || x>(B)) goto BAD_FMT; \ - else (C) = x; \ - } while (0) - - -#define format_debug(A,B) do { \ - if (debug_format_file) { \ - fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ - } \ - } while (0) - -#define undump_size(A,B,C,D) do { \ - undump_int(x); \ - if (x<(A)) goto BAD_FMT; \ - if (x>(B)) too_small(C); \ - else format_debug (C,x); \ - (D) = x; \ - } while (0) - - -@ @c -boolean load_fmt_file(const char *fmtname) -{ - int j, k; /* all-purpose indices */ - halfword p; /* all-purpose pointer */ - int x; /* something undumped */ - char *format_engine; - /* Undump constants for consistency check */ - if (ini_version) { - libcfree(hash); - libcfree(eqtb); - libcfree(fixmem); - libcfree(varmem); - } - undump_int(x); - format_debug("format magic number", x); - if (x != 0x57325458) - goto BAD_FMT; /* not a format file */ - - undump_int(x); - format_debug("format id", x); - if (x != FORMAT_ID) - goto BAD_FMT; /* FORMAT_ID mismatch */ - - undump_int(x); - format_debug("engine name size", x); - if ((x < 0) || (x > 256)) - goto BAD_FMT; /* corrupted format file */ - - format_engine = xmalloc((unsigned) x); - undump_things(format_engine[0], x); - format_engine[x - 1] = 0; /* force string termination, just in case */ - if (strcmp(engine_name, format_engine)) { - wake_up_terminal(); - wterm_cr(); - fprintf(term_out, "---! %s was written by %s", fmtname, format_engine); - xfree(format_engine); - goto BAD_FMT; - } - xfree(format_engine); - undump_int(x); - format_debug("string pool checksum", x); - if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */ - wake_up_terminal(); - wterm_cr(); - fprintf(term_out, "---! %s was written by a different version", - fmtname); - goto BAD_FMT; - } - undump_int(x); - if (x != max_halfword) - goto BAD_FMT; /* check |max_halfword| */ - undump_int(hash_high); - if ((hash_high < 0) || (hash_high > sup_hash_extra)) - goto BAD_FMT; - if (hash_extra < hash_high) - hash_extra = hash_high; - eqtb_top = eqtb_size + hash_extra; - if (hash_extra == 0) - hash_top = undefined_control_sequence; - else - hash_top = eqtb_top; - hash = xmallocarray(two_halves, (unsigned) (1 + hash_top)); - memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); - eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); - set_eq_type(undefined_control_sequence, undefined_cs_cmd); - set_equiv(undefined_control_sequence, null); - set_eq_level(undefined_control_sequence, level_zero); - for (x = eqtb_size + 1; x <= eqtb_top; x++) - eqtb[x] = eqtb[undefined_control_sequence]; - undump_int(x); - if (x != eqtb_size) - goto BAD_FMT; - undump_int(x); - if (x != hash_prime) - goto BAD_FMT; - - /* Undump the string pool */ - str_ptr = undump_string_pool(); - /* Undump the dynamic memory */ - undump_node_mem(); - undump_int(temp_token_head); - undump_int(hold_token_head); - undump_int(omit_template); - undump_int(null_list); - undump_int(backup_head); - undump_int(garbage); - undump_int(fix_mem_min); - undump_int(fix_mem_max); - fixmem = xmallocarray(smemory_word, fix_mem_max + 1); - memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word)); - undump_int(fix_mem_end); - undump_int(avail); - undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); - undump_int(dyn_used); - - /* Undump the table of equivalents */ - /* Undump regions 1 to 6 of |eqtb| */ - k = null_cs; - do { - undump_int(x); - if ((x < 1) || (k + x > eqtb_size + 1)) - goto BAD_FMT; - undump_things(eqtb[k], x); - k = k + x; - undump_int(x); - if ((x < 0) || (k + x > eqtb_size + 1)) - goto BAD_FMT; - for (j = k; j <= k + x - 1; j++) - eqtb[j] = eqtb[k - 1]; - k = k + x; - } while (k <= eqtb_size); - if (hash_high > 0) /* undump |hash_extra| part */ - undump_things(eqtb[eqtb_size + 1], hash_high); - - undump(hash_base, hash_top, par_loc); - par_token = cs_token_flag + par_loc; - undump(hash_base, hash_top, write_loc); - undump_math_codes(); - undump_text_codes(); - /* Undump the hash table */ - undump_primitives(); - undump(hash_base, frozen_control_sequence, hash_used); - p = hash_base - 1; - do { - undump(p + 1, hash_used, p); - undump_hh(hash[p]); - } while (p != hash_used); - undump_things(hash[hash_used + 1], - undefined_control_sequence - 1 - hash_used); - if (debug_format_file) - print_csnames(hash_base, undefined_control_sequence - 1); - if (hash_high > 0) { - undump_things(hash[eqtb_size + 1], hash_high); - if (debug_format_file) - print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); - } - undump_int(cs_count); - - /* Undump the font information */ - undump_int(x); - set_max_font_id(x); - for (k = 0; k <= max_font_id(); k++) { - /* Undump the array info for internal font number |k| */ - undump_font(k); - } - undump_math_data(); - - /* Undump the hyphenation tables */ - undump_language_data(); - - /* Undump a couple more things and the closing check word */ - undump(batch_mode, error_stop_mode, interaction); - if (interactionoption != unspecified_mode) - interaction = interactionoption; - undump(0, str_ptr, format_ident); - undump(0, str_ptr, format_name); - undump_int(x); - if (x != 69069) - goto BAD_FMT; - - /* Undump the lua bytecodes */ - undump_luac_registers(); - - prev_depth_par = ignore_depth; - return true; /* it worked! */ - BAD_FMT: - wake_up_terminal(); - wterm_cr(); - fprintf(term_out, "(Fatal format file error; I'm stymied)"); - return false; -} +% dumpdata.w +% +% Copyright 2009-2010 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c + +#include "ptexlib.h" + +/* we start with 907: the sum of the values of the bytes of "don knuth" */ + +#define FORMAT_ID (907+27) +#if ((FORMAT_ID>=0) && (FORMAT_ID<=256)) +#error Wrong value for FORMAT_ID. +#endif + + +@ After \.{INITEX} has seen a collection of fonts and macros, it +can write all the necessary information on an auxiliary file so +that production versions of \TeX\ are able to initialize their +memory at high speed. The present section of the program takes +care of such output and input. We shall consider simultaneously +the processes of storing and restoring, +so that the inverse relation between them is clear. +@.INITEX@> + +The global variable |format_ident| is a string that is printed right +after the |banner| line when \TeX\ is ready to start. For \.{INITEX} this +string says simply `\.{(INITEX)}'; for other versions of \TeX\ it says, +for example, `\.{(preloaded format=plain 1982.11.19)}', showing the year, +month, and day that the format file was created. We have |format_ident=0| +before \TeX's tables are loaded. |FORMAT_ID| is a new field of type int +suitable for the identification of a format: values between 0 and 256 +(included) can not be used because in the previous format they are used +for the length of the name of the engine. +@c +str_number format_ident; +str_number format_name; /* principal file name */ + + +@ Format files consist of |memory_word| items, and we use the following +macros to dump words of different types: + +@c +FILE *fmt_file; /* for input or output of format information */ + +@ @c +void store_fmt_file(void) +{ + int j, k, l; /* all-purpose indices */ + halfword p; /* all-purpose pointer */ + int x; /* something to dump */ + char *format_engine; + int callback_id; /* |pre_dump| callback */ + char *fmtname = NULL; + /* If dumping is not allowed, abort */ + /* The user is not allowed to dump a format file unless |save_ptr=0|. + This condition implies that |cur_level=level_one|, hence + the |xeq_level| array is constant and it need not be dumped. */ + if (save_ptr != 0) { + print_err("You can't dump inside a group"); + help1("`{...\\dump}' is a no-no."); + succumb(); + } + + /* Create the |format_ident|, open the format file, and inform the user + that dumping has begun */ + callback_id = callback_defined(pre_dump_callback); + if (callback_id > 0) { + (void) run_callback(callback_id, "->"); + } + selector = new_string; + tprint(" (format="); + print(job_name); + print_char(' '); + print_int(year_par); + print_char('.'); + print_int(month_par); + print_char('.'); + print_int(day_par); + print_char(')'); + str_room(2); + format_ident = make_string(); + print(job_name); + format_name = make_string(); + if (interaction == batch_mode) + selector = log_only; + else + selector = term_and_log; + + fmtname = pack_job_name(format_extension); + while (!zopen_w_output(&fmt_file, fmtname, FOPEN_WBIN_MODE)) { + fmtname = prompt_file_name("format file name", format_extension); + } + tprint_nl("Beginning to dump on file "); + tprint(fmtname); + free(fmtname); + tprint_nl(""); + print(format_ident); + + /* Dump constants for consistency check */ + /* The next few sections of the program should make it clear how we use the + dump/undump macros. */ + + dump_int(0x57325458); /* Web2C \TeX's magic constant: "W2TX" */ + dump_int(FORMAT_ID); + + /* Align engine to 4 bytes with one or more trailing NUL */ + x = (int) strlen(engine_name); + format_engine = xmalloc((unsigned) (x + 4)); + strcpy(format_engine, engine_name); + for (k = x; k <= x + 3; k++) + format_engine[k] = 0; + x = x + 4 - (x % 4); + dump_int(x); + dump_things(format_engine[0], x); + xfree(format_engine); + dump_int(0x57325458); /* TODO HM, what checksum would make sense? */ + dump_int(max_halfword); + dump_int(hash_high); + dump_int(eqtb_size); + dump_int(hash_prime); + + /* Dump the string pool */ + k = dump_string_pool(); + print_ln(); + print_int(k); + tprint(" strings using "); + print_int((longinteger) pool_size); + tprint(" bytes"); + + /* Dump the dynamic memory */ + /* By sorting the list of available spaces in the variable-size portion of + |mem|, we are usually able to get by without having to dump very much + of the dynamic memory. + + We recompute |var_used| and |dyn_used|, so that \.{INITEX} dumps valid + information even when it has not been gathering statistics. + */ + dump_node_mem(); + dump_int(temp_token_head); + dump_int(hold_token_head); + dump_int(omit_template); + dump_int(null_list); + dump_int(backup_head); + dump_int(garbage); + x = (int) fix_mem_min; + dump_int(x); + x = (int) fix_mem_max; + dump_int(x); + x = (int) fix_mem_end; + dump_int(x); + dump_int(avail); + dyn_used = (int) fix_mem_end + 1; + dump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); + x = x + (int) (fix_mem_end + 1 - fix_mem_min); + p = avail; + while (p != null) { + decr(dyn_used); + p = token_link(p); + } + dump_int(dyn_used); + print_ln(); + print_int(x); + tprint(" memory locations dumped; current usage is "); + print_int(var_used); + print_char('&'); + print_int(dyn_used); + + /* Dump the table of equivalents */ + /* Dump regions 1 to 4 of |eqtb| */ + /*The table of equivalents usually contains repeated information, so we dump it + in compressed form: The sequence of $n+2$ values $(n,x_1,\ldots,x_n,m)$ in the + format file represents $n+m$ consecutive entries of |eqtb|, with |m| extra + copies of $x_n$, namely $(x_1,\ldots,x_n,x_n,\ldots,x_n)$. + */ + k = null_cs; + do { + j = k; + while (j < int_base - 1) { + if ((equiv(j) == equiv(j + 1)) && (eq_type(j) == eq_type(j + 1)) && + (eq_level(j) == eq_level(j + 1))) + goto FOUND1; + incr(j); + } + l = int_base; + goto DONE1; /* |j=int_base-1| */ + FOUND1: + incr(j); + l = j; + while (j < int_base - 1) { + if ((equiv(j) != equiv(j + 1)) || (eq_type(j) != eq_type(j + 1)) || + (eq_level(j) != eq_level(j + 1))) + goto DONE1; + incr(j); + } + DONE1: + dump_int(l - k); + dump_things(eqtb[k], l - k); + k = j + 1; + dump_int(k - l); + } while (k != int_base); + + /* Dump regions 5 and 6 of |eqtb| */ + do { + j = k; + while (j < eqtb_size) { + if (eqtb[j].cint == eqtb[j + 1].cint) + goto FOUND2; + incr(j); + } + l = eqtb_size + 1; + goto DONE2; /* |j=eqtb_size| */ + FOUND2: + incr(j); + l = j; + while (j < eqtb_size) { + if (eqtb[j].cint != eqtb[j + 1].cint) + goto DONE2; + incr(j); + } + DONE2: + dump_int(l - k); + dump_things(eqtb[k], l - k); + k = j + 1; + dump_int(k - l); + } while (k <= eqtb_size); + if (hash_high > 0) + dump_things(eqtb[eqtb_size + 1], hash_high); /* dump |hash_extra| part */ + + dump_int(par_loc); + dump_int(write_loc); + dump_math_codes(); + dump_text_codes(); + /* Dump the hash table */ + /* A different scheme is used to compress the hash table, since its lower + region is usually sparse. When |text(p)<>0| for |p<=hash_used|, we output + two words, |p| and |hash[p]|. The hash table is, of course, densely packed + for |p>=hash_used|, so the remaining entries are output in a~block. + */ + dump_primitives(); + dump_int(hash_used); + cs_count = frozen_control_sequence - 1 - hash_used + hash_high; + for (p = hash_base; p <= hash_used; p++) { + if (cs_text(p) != 0) { + dump_int(p); + dump_hh(hash[p]); + incr(cs_count); + } + } + dump_things(hash[hash_used + 1], + undefined_control_sequence - 1 - hash_used); + if (hash_high > 0) + dump_things(hash[eqtb_size + 1], hash_high); + dump_int(cs_count); + print_ln(); + print_int(cs_count); + tprint(" multiletter control sequences"); + + /* Dump the font information */ + dump_int(max_font_id()); + for (k = 0; k <= max_font_id(); k++) { + /* Dump the array info for internal font number |k| */ + dump_font(k); + tprint_nl("\\font"); + print_esc(font_id_text(k)); + print_char('='); + tprint_file_name((unsigned char *) font_name(k), + (unsigned char *) font_area(k), NULL); + if (font_size(k) != font_dsize(k)) { + tprint(" at "); + print_scaled(font_size(k)); + tprint("pt"); + } + } + print_ln(); + print_int(max_font_id()); + tprint(" preloaded font"); + if (max_font_id() != 1) + print_char('s'); + dump_math_data(); + + /* Dump the hyphenation tables */ + dump_language_data(); + + /* Dump a couple more things and the closing check word */ + dump_int(interaction); + dump_int(format_ident); + dump_int(format_name); + dump_int(69069); + /* We have already printed a lot of statistics, so we set |tracing_stats:=0| + to prevent them from appearing again. */ + tracing_stats_par = 0; + + /* Dump the lua bytecodes */ + dump_luac_registers(); + + /* Close the format file */ + zwclose(fmt_file); +} + +@ Corresponding to the procedure that dumps a format file, we have a function +that reads one in. The function returns |false| if the dumped format is +incompatible with the present \TeX\ table sizes, etc. + +@c +#define too_small(A) do { \ + wake_up_terminal(); \ + wterm_cr(); \ + fprintf(term_out,"---! Must increase the %s",(A)); \ + goto BAD_FMT; \ + } while (0) + +@ The inverse macros are slightly more complicated, since we need to check +the range of the values we are reading in. We say `|undump(a)(b)(x)|' to +read an integer value |x| that is supposed to be in the range |a<=x<=b|. + +@c +#define undump(A,B,C) do { \ + undump_int(x); \ + if (x<(A) || x>(B)) goto BAD_FMT; \ + else (C) = x; \ + } while (0) + + +#define format_debug(A,B) do { \ + if (debug_format_file) { \ + fprintf (stderr, "fmtdebug: %s=%d", (A), (int)(B)); \ + } \ + } while (0) + +#define undump_size(A,B,C,D) do { \ + undump_int(x); \ + if (x<(A)) goto BAD_FMT; \ + if (x>(B)) too_small(C); \ + else format_debug (C,x); \ + (D) = x; \ + } while (0) + + +@ @c +boolean load_fmt_file(const char *fmtname) +{ + int j, k; /* all-purpose indices */ + halfword p; /* all-purpose pointer */ + int x; /* something undumped */ + char *format_engine; + /* Undump constants for consistency check */ + if (ini_version) { + libcfree(hash); + libcfree(eqtb); + libcfree(fixmem); + libcfree(varmem); + } + undump_int(x); + format_debug("format magic number", x); + if (x != 0x57325458) + goto BAD_FMT; /* not a format file */ + + undump_int(x); + format_debug("format id", x); + if (x != FORMAT_ID) + goto BAD_FMT; /* FORMAT_ID mismatch */ + + undump_int(x); + format_debug("engine name size", x); + if ((x < 0) || (x > 256)) + goto BAD_FMT; /* corrupted format file */ + + format_engine = xmalloc((unsigned) x); + undump_things(format_engine[0], x); + format_engine[x - 1] = 0; /* force string termination, just in case */ + if (strcmp(engine_name, format_engine)) { + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by %s", fmtname, format_engine); + xfree(format_engine); + goto BAD_FMT; + } + xfree(format_engine); + undump_int(x); + format_debug("string pool checksum", x); + if (x != 0x57325458) { /* todo: @@\$ *//* check that strings are the same */ + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "---! %s was written by a different version", + fmtname); + goto BAD_FMT; + } + undump_int(x); + if (x != max_halfword) + goto BAD_FMT; /* check |max_halfword| */ + undump_int(hash_high); + if ((hash_high < 0) || (hash_high > sup_hash_extra)) + goto BAD_FMT; + if (hash_extra < hash_high) + hash_extra = hash_high; + eqtb_top = eqtb_size + hash_extra; + if (hash_extra == 0) + hash_top = undefined_control_sequence; + else + hash_top = eqtb_top; + hash = xmallocarray(two_halves, (unsigned) (1 + hash_top)); + memset(hash, 0, sizeof(two_halves) * (unsigned) (hash_top + 1)); + eqtb = xmallocarray(memory_word, (unsigned) (eqtb_top + 1)); + set_eq_type(undefined_control_sequence, undefined_cs_cmd); + set_equiv(undefined_control_sequence, null); + set_eq_level(undefined_control_sequence, level_zero); + for (x = eqtb_size + 1; x <= eqtb_top; x++) + eqtb[x] = eqtb[undefined_control_sequence]; + undump_int(x); + if (x != eqtb_size) + goto BAD_FMT; + undump_int(x); + if (x != hash_prime) + goto BAD_FMT; + + /* Undump the string pool */ + str_ptr = undump_string_pool(); + /* Undump the dynamic memory */ + undump_node_mem(); + undump_int(temp_token_head); + undump_int(hold_token_head); + undump_int(omit_template); + undump_int(null_list); + undump_int(backup_head); + undump_int(garbage); + undump_int(fix_mem_min); + undump_int(fix_mem_max); + fixmem = xmallocarray(smemory_word, fix_mem_max + 1); + memset(voidcast(fixmem), 0, (fix_mem_max + 1) * sizeof(smemory_word)); + undump_int(fix_mem_end); + undump_int(avail); + undump_things(fixmem[fix_mem_min], fix_mem_end - fix_mem_min + 1); + undump_int(dyn_used); + + /* Undump the table of equivalents */ + /* Undump regions 1 to 6 of |eqtb| */ + k = null_cs; + do { + undump_int(x); + if ((x < 1) || (k + x > eqtb_size + 1)) + goto BAD_FMT; + undump_things(eqtb[k], x); + k = k + x; + undump_int(x); + if ((x < 0) || (k + x > eqtb_size + 1)) + goto BAD_FMT; + for (j = k; j <= k + x - 1; j++) + eqtb[j] = eqtb[k - 1]; + k = k + x; + } while (k <= eqtb_size); + if (hash_high > 0) /* undump |hash_extra| part */ + undump_things(eqtb[eqtb_size + 1], hash_high); + + undump(hash_base, hash_top, par_loc); + par_token = cs_token_flag + par_loc; + undump(hash_base, hash_top, write_loc); + undump_math_codes(); + undump_text_codes(); + /* Undump the hash table */ + undump_primitives(); + undump(hash_base, frozen_control_sequence, hash_used); + p = hash_base - 1; + do { + undump(p + 1, hash_used, p); + undump_hh(hash[p]); + } while (p != hash_used); + undump_things(hash[hash_used + 1], + undefined_control_sequence - 1 - hash_used); + if (debug_format_file) + print_csnames(hash_base, undefined_control_sequence - 1); + if (hash_high > 0) { + undump_things(hash[eqtb_size + 1], hash_high); + if (debug_format_file) + print_csnames(eqtb_size + 1, hash_high - (eqtb_size + 1)); + } + undump_int(cs_count); + + /* Undump the font information */ + undump_int(x); + set_max_font_id(x); + for (k = 0; k <= max_font_id(); k++) { + /* Undump the array info for internal font number |k| */ + undump_font(k); + } + undump_math_data(); + + /* Undump the hyphenation tables */ + undump_language_data(); + + /* Undump a couple more things and the closing check word */ + undump(batch_mode, error_stop_mode, interaction); + if (interactionoption != unspecified_mode) + interaction = interactionoption; + undump(0, str_ptr, format_ident); + undump(0, str_ptr, format_name); + undump_int(x); + if (x != 69069) + goto BAD_FMT; + + /* Undump the lua bytecodes */ + undump_luac_registers(); + + prev_depth_par = ignore_depth; + return true; /* it worked! */ + BAD_FMT: + wake_up_terminal(); + wterm_cr(); + fprintf(term_out, "(Fatal format file error; I'm stymied)"); + return false; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/equivalents.h b/Build/source/texk/web2c/luatexdir/tex/equivalents.h index 80f8108393f..d7ca036e25a 100644 --- a/Build/source/texk/web2c/luatexdir/tex/equivalents.h +++ b/Build/source/texk/web2c/luatexdir/tex/equivalents.h @@ -281,14 +281,18 @@ the |number_regs| \.{\\dimen} registers. # define math_nolimits_mode_code 90 # define math_rules_mode_code 91 # define math_rules_fam_code 92 -# define synctex_code 93 /* is synctex file generation enabled ? */ -# define shape_mode_code 94 -# define first_valid_language_code 95 -# define hyphenation_bounds_code 96 -# define math_skip_mode_code 97 -# define math_pre_display_gap_factor_code 98 - -# define math_option_code (math_skip_mode_code+1) +# define math_italics_mode_code 93 +# define synctex_code 94 /* is synctex file generation enabled ? */ +# define shape_mode_code 95 +# define first_valid_language_code 96 +# define hyphenation_bounds_code 97 +# define math_skip_mode_code 98 +# define math_pre_display_gap_factor_code 99 +# define hyphen_penalty_mode_code 100 +# define automatic_hyphen_penalty_code 101 +# define explicit_hyphen_penalty_code 102 + +# define math_option_code (explicit_hyphen_penalty_code+1) # define mathoption_int_base_code (math_option_code+1) /* one reserve */ # define mathoption_int_last_code (mathoption_int_base_code+8) @@ -648,8 +652,9 @@ extern halfword last_cs_name; #define disable_lig_par int_par(disable_lig_code) #define disable_kern_par int_par(disable_kern_code) #define disable_space_par int_par(disable_space_code) -#define scripts_mode_par int_par(math_scripts_mode_code) -#define nolimits_mode_par int_par(math_nolimits_mode_code) +#define math_scripts_mode_par int_par(math_scripts_mode_code) +#define math_nolimits_mode_par int_par(math_nolimits_mode_code) +#define math_italics_mode_par int_par(math_italics_mode_code) #define math_rules_mode_par int_par(math_rules_mode_code) #define math_rules_fam_par int_par(math_rules_fam_code) @@ -765,6 +770,10 @@ extern halfword last_cs_name; #define default_skew_char_par int_par(default_skew_char_code) #define saving_hyph_codes_par int_par(saving_hyph_codes_code) +#define hyphen_penalty_mode_par int_par(hyphen_penalty_mode_code) +#define automatic_hyphen_penalty_par int_par(automatic_hyphen_penalty_code) +#define explicit_hyphen_penalty_par int_par(explicit_hyphen_penalty_code) + #define cur_lang_par int_par(cur_lang_code) #define cur_font_par equiv(cur_font_loc) @@ -802,4 +811,71 @@ extern halfword last_cs_name; #define xspace_skip_subtype (xspace_skip_code + 1) #define space_skip_subtype (space_skip_code + 1) +/* + +hyphen_penalty_mode_par automatic_disc (-) explicit_disc (\-) +--------------------------------------------------------------------------------- +0 (default) ex_hyphen_penalty_par ex_hyphen_penalty_par +1 hyphen_penalty_par hyphen_penalty_par +2 ex_hyphen_penalty_par hyphen_penalty_par +3 hyphen_penalty_par ex_hyphen_penalty_par +4 automatic_hyphen_penalty_par explicit_disc_penalty_par +5 ex_hyphen_penalty_par explicit_disc_penalty_par +6 hyphen_penalty_par explicit_disc_penalty_par +7 automatic_hyphen_penalty_par ex_hyphen_penalty_par +8 automatic_hyphen_penalty_par hyphen_penalty_par +*/ + +#define set_automatic_disc_penalty(n) \ + switch (hyphen_penalty_mode_par) { \ + case 0: \ + case 2: \ + case 5: \ + /* we take ex_hyphen_penalty */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + case 1: \ + case 3: \ + case 6: \ + /* we take hyphen_penalty */ \ + disc_penalty(n) = hyphen_penalty_par; \ + break; \ + case 4: \ + case 7: \ + case 8: \ + /* we take automatic_hyphen_penalty */ \ + disc_penalty(n) = automatic_hyphen_penalty_par; \ + break; \ + default: \ + /* what we've done since the beginning */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + } + +#define set_explicit_disc_penalty(n) \ + switch (hyphen_penalty_mode_par) { \ + case 0: \ + case 3: \ + case 7: \ + /* we take ex_hyphen_penalty */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + case 1: \ + case 2: \ + case 8: \ + /* we take hyphen_penalty */ \ + disc_penalty(n) = hyphen_penalty_par; \ + break; \ + case 4: \ + case 5: \ + case 6: \ + /* we take automatic_hyphen_penalty */ \ + disc_penalty(n) = explicit_hyphen_penalty_par; \ + break; \ + default: \ + /* what we've done since the beginning */ \ + disc_penalty(n) = ex_hyphen_penalty_par; \ + break; \ + } + #endif diff --git a/Build/source/texk/web2c/luatexdir/tex/linebreak.w b/Build/source/texk/web2c/luatexdir/tex/linebreak.w index ad003097105..48705ad8cad 100644 --- a/Build/source/texk/web2c/luatexdir/tex/linebreak.w +++ b/Build/source/texk/web2c/luatexdir/tex/linebreak.w @@ -104,14 +104,14 @@ void line_break(boolean d, int line_break_context) new_hyphenation(temp_head, cur_list.tail_field); cur_list.tail_field = new_ligkern(temp_head, cur_list.tail_field); if (is_char_node(cur_list.tail_field)) { - tail_append(new_penalty(inf_penalty)); + tail_append(new_penalty(inf_penalty,line_penalty)); } else if (type(cur_list.tail_field) != glue_node) { - tail_append(new_penalty(inf_penalty)); + tail_append(new_penalty(inf_penalty,line_penalty)); } else { halfword t = alink(cur_list.tail_field); flush_node(cur_list.tail_field); cur_list.tail_field = t; - tail_append(new_penalty(inf_penalty)); + tail_append(new_penalty(inf_penalty,line_penalty)); } final_par_glue = new_param_glue(par_fill_skip_code); couple_nodes(cur_list.tail_field, final_par_glue); diff --git a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w b/Build/source/texk/web2c/luatexdir/tex/maincontrol.w index 45e9bb46934..0baba872e8e 100644 --- a/Build/source/texk/web2c/luatexdir/tex/maincontrol.w +++ b/Build/source/texk/web2c/luatexdir/tex/maincontrol.w @@ -1,3639 +1,3639 @@ -% maincontrol.w -% -% Copyright 2009-2010 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c - -#include "ptexlib.h" -#include "lua/luatex-api.h" - -@ @c -#define mode mode_par -#define tail tail_par -#define head head_par -#define dir_save dirs_par - -@ We come now to the |main_control| routine, which contains the master -switch that causes all the various pieces of \TeX\ to do their things, -in the right order. - -In a sense, this is the grand climax of the program: It applies all the -tools that we have worked so hard to construct. In another sense, this is -the messiest part of the program: It necessarily refers to other pieces -of code all over the place, so that a person can't fully understand what is -going on without paging back and forth to be reminded of conventions that -are defined elsewhere. We are now at the hub of the web, the central nervous -system that touches most of the other parts and ties them together. -@^brain@> - -The structure of |main_control| itself is quite simple. There's a label -called |big_switch|, at which point the next token of input is fetched -using |get_x_token|. Then the program branches at high speed into one of -about 100 possible directions, based on the value of the current -mode and the newly fetched command code; the sum |abs(mode)+cur_cmd| -indicates what to do next. For example, the case `|vmode+letter|' arises -when a letter occurs in vertical mode (or internal vertical mode); this -case leads to instructions that initialize a new paragraph and enter -horizontal mode. - -The big |case| statement that contains this multiway switch has been labeled -|reswitch|, so that the program can |goto reswitch| when the next token -has already been fetched. Most of the cases are quite short; they call -an ``action procedure'' that does the work for that case, and then they -either |goto reswitch| or they ``fall through'' to the end of the |case| -statement, which returns control back to |big_switch|. Thus, |main_control| -is not an extremely large procedure, in spite of the multiplicity of things -it must do; it is small enough to be handled by PASCAL compilers that put -severe restrictions on procedure size. -@!@^action procedure@> - -One case is singled out for special treatment, because it accounts for most -of \TeX's activities in typical applications. The process of reading simple -text and converting it into |char_node| records, while looking for ligatures -and kerns, is part of \TeX's ``inner loop''; the whole program runs -efficiently when its inner loop is fast, so this part has been written -with particular care. - -We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we -set it equal to |sf_code(cur_chr)|, except that it should never change -from a value less than 1000 to a value exceeding 1000. The most common -case is |sf_code(cur_chr)=1000|, so we want that case to be fast. - -@c -void adjust_space_factor(void) -{ - halfword s = get_sf_code(cur_chr); - if (s == 1000) { - space_factor_par = 1000; - } else if (s < 1000) { - if (s > 0) - space_factor_par = s; - } else if (space_factor_par < 1000) { - space_factor_par = 1000; - } else { - space_factor_par = s; - } -} - -@ From Knuth: ``Having |font_glue| allocated for each text font saves -both time and memory.'' That may be true, but it also punches through -the API wall for fonts, so I removed that -- Taco. But a bit of caching -is very welcome, which is why I need to have the next two globals: - -@ To handle the execution state of |main_control|'s eternal loop, -an extra global variable is used, along with a macro to define -its values. - -@c -#define goto_next 0 -#define goto_skip_token 1 -#define goto_return 2 - -static int main_control_state; - -@* Main control helpers. - -Here are all the functions that are called from |main_control| that -are not already defined elsewhere. For the moment, this list simply -in the order that the appear in |init_main_control|, below. - -@ -@c -static void run_char_num (void) { - scan_char_num(); - cur_chr = cur_val; - adjust_space_factor(); - tail_append(new_char(cur_font_par, cur_chr)); -} - -static void run_char (void) { - adjust_space_factor(); - tail_append(new_char(cur_font_par, cur_chr)); -} - -@ -The occurrence of blank spaces is almost part of \TeX's inner loop, -since we usually encounter about one space for every five non-blank characters. -Therefore |main_control| gives second-highest priority to ordinary spaces. - -When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will -see to it later that the corresponding glue specification is precisely -|zero_glue|, not merely a pointer to some specification that happens -to be full of zeroes. Therefore it is simple to test whether a glue parameter -is zero or~not. - -@c -static void run_app_space (void) { - halfword p; /* was a global temp_ptr */ - int method = disable_space_par ; - if (method == 1) { - /* don't inject anything, not even zero skip */ - } else if (method == 2) { - p = new_glue(zero_glue); - couple_nodes(tail,p); - tail = p; - } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) { - app_space(); - } else { - /* Append a normal inter-word space to the current list */ - if (glue_is_zero(space_skip_par)) { - /* Find the glue specification for text spaces in the current font */ - p = new_glue(zero_glue); - width(p) = space(cur_font_par); - stretch(p) = space_stretch(cur_font_par); - shrink(p) = space_shrink(cur_font_par); - - } else { - p = new_param_glue(space_skip_code); - } - /* so from now we have a subtype with spaces: */ - subtype(p) = space_skip_code + 1 ; - couple_nodes(tail,p); - tail = p; - } -} - -@ Append a |boundary_node| -@c -static void run_boundary (void) { - halfword n ; - n = new_node(boundary_node,cur_chr); - if ((cur_chr == 1) || (cur_chr == 2) ) { - /* user boundary or protrusion boundary */ - scan_int(); - boundary_value(n) = cur_val; - } - couple_nodes(tail, n); - tail = n; -} - -@ @c -static void run_char_ghost (void) { - int t; - t = cur_chr; - get_x_token(); - if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd) - || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) { - halfword p = new_glyph(get_cur_font(), cur_chr); - if (t == 0) { - set_is_leftghost(p); - } else { - set_is_rightghost(p); - } - tail_append(p); - } -} - -@ @c -static void run_relax (void) { - return; -} - -@ |ignore_spaces| is a special case: after it has acted, |get_x_token| has already -fetched the next token from the input, so that operation in |main_control| -should be skipped. - -@c -static void run_ignore_spaces (void) { - if (cur_chr == 0) { - /* Get the next non-blank non-call... */ - do { - get_x_token(); - } while (cur_cmd == spacer_cmd); - main_control_state = goto_skip_token; - } else { - int t = scanner_status; - scanner_status = normal; - get_next(); - scanner_status = t; - cur_cs = prim_lookup(cs_text(cur_cs)); - if (cur_cs != undefined_primitive) { - cur_cmd = get_prim_eq_type(cur_cs); - cur_chr = get_prim_equiv(cur_cs); - cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr; - main_control_state = goto_skip_token; - } - } -} - -@ |stop| is the second special case. We want |main_control| to return to its caller -if there is nothing left to do. - -@c -static void run_stop (void) { - if (its_all_over()) - main_control_state= goto_return; /* this is the only way out */ -} - -@ @c -static void run_non_math_math (void) { - back_input(); - new_graf(true); -} - -@ @c -static void run_math_char_num (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - if (cur_chr == 0) - mval = scan_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_mathchar(umath_mathcode); - else - mval = scan_mathchar(umathnum_mathcode); - math_char_in_text(mval); -} - -@ @c -static void run_math_given (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, tex_mathcode); - math_char_in_text(mval); -} - -static void run_xmath_given (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, umath_mathcode); - math_char_in_text(mval); -} - -@ The most important parts of |main_control| are concerned with \TeX's -chief mission of box-making. We need to control the activities that put -entries on vlists and hlists, as well as the activities that convert -those lists into boxes. All of the necessary machinery has already been -developed; it remains for us to ``push the buttons'' at the right times. - -As an introduction to these routines, let's consider one of the simplest -cases: What happens when `\.{\\hrule}' occurs in vertical mode, or -`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control| -is short, since the |scan_rule_spec| routine already does most of what is -required; thus, there is no need for a special action procedure. - -Note that baselineskip calculations are disabled after a rule in vertical -mode, by setting |prev_depth:=ignore_depth|. - -@c -static void run_rule (void) { - tail_append(scan_rule_spec()); - if (abs(mode) == vmode) - prev_depth_par = ignore_depth; - else if (abs(mode) == hmode) - space_factor_par = 1000; -} - -@ -Many of the actions related to box-making are triggered by the appearance -of braces in the input. For example, when the user says `\.{\\hbox} -\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, -the information about the box size (100pt, |exactly|) is put onto |save_stack| -with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|; -\TeX\ enters restricted horizontal mode to process the hlist. The right -brace eventually causes |save_stack| to be restored to its former state, -at which time the information about the box size (100pt, |exactly|) is -available once again; a box is packaged and we leave restricted horizontal -mode, appending the new box to the current list of the enclosing mode -(in this case to the current list of vertical mode), followed by any -vertical adjustments that were removed from the box by |hpack|. - -The next few sections of the program are therefore concerned with the -treatment of left and right curly braces. - -If a left brace occurs in the middle of a page or paragraph, it simply -introduces a new level of grouping, and the matching right brace will not have -such a drastic effect. Such grouping affects neither the mode nor the -current list. - -@c -static void run_left_brace (void) { - new_save_level(simple_group); - eq_word_define(int_base + no_local_whatsits_code, 0); - eq_word_define(int_base + no_local_dirs_code, 0); -} - -static void run_begin_group (void) { - new_save_level(semi_simple_group); - eq_word_define(int_base + no_local_whatsits_code, 0); - eq_word_define(int_base + no_local_dirs_code, 0); -} - -static void run_end_group (void) { - if (cur_group == semi_simple_group) { - fixup_directions(); - } else { - off_save(); - } -} - -@ Constructions that require a box are started by calling |scan_box| with -a specified context code. The |scan_box| routine verifies -that a |make_box| command comes next and then it calls |begin_box|. - -@c -static void run_move (void) { - int t = cur_chr; - scan_normal_dimen(); - if (t == 0) - scan_box(cur_val); - else - scan_box(-cur_val); -} - -@ @c -static void run_leader_ship (void) { - scan_box(leader_flag - a_leaders + cur_chr); -} - -@ @c -static void run_make_box (void) { - begin_box(0); -} - -@ @c -static void run_box_dir (void) { - scan_register_num(); - cur_box = box(cur_val); - scan_optional_equals(); - scan_direction(); - if (cur_box != null) - box_dir(cur_box) = cur_val; -} - -@ There is a really small patch to add a new primitive called -\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent}, -but in horizontal and math modes it is really a no-op (as opposed to -\.{\\indent}, which executes the |indent_in_hmode| procedure). - -A paragraph begins when horizontal-mode material occurs in vertical mode, -or when the paragraph is explicitly started by `\.{\\quitvmode}', -`\.{\\indent}' or `\.{\\noindent}'. - -@c -static void run_start_par_vmode (void) { - new_graf((cur_chr > 0)); -} - -@ @c -static void run_start_par (void) { - if (cur_chr != 2) - indent_in_hmode(); -} - -@ @c -static void run_new_graf (void) { - back_input(); - new_graf(true); -} - -@ A paragraph ends when a |par_end| command is sensed, or when we are in -horizontal mode when reaching the right brace of vertical-mode routines -like \.{\\vbox}, \.{\\insert}, or \.{\\output}. - -@c -static void run_par_end_vmode (void) { - normal_paragraph(); - if (mode > 0) { - checked_page_filter(vmode_par); - build_page(); - } -} - -@ @c -static void run_par_end_hmode (void) { - if (align_state < 0) - off_save(); /* this tries to recover from an alignment that didn't end properly */ - end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */ - if (mode == vmode) { - checked_page_filter(hmode_par); - build_page(); - } -} - -@ @c -static void append_italic_correction_mmode (void) { - tail_append(new_kern(0)); /* what subtype to use */ -} - -@ @c -static void run_local_box (void) { - append_local_box(cur_chr); -} - -@ @c -static void run_halign_mmode (void) { - if (privileged()) { - if (cur_group == math_shift_group) - init_align(); - else - off_save(); - } -} - -@ @c -static void run_eq_no (void) { - if (privileged()) { - if (cur_group == math_shift_group) - start_eq_no(); - else - off_save(); - } -} - -@ @c -static void run_letter_mmode (void) { - set_math_char(get_math_code(cur_chr)); -} - -@ @c -static void run_char_num_mmode (void) { - scan_char_num(); - cur_chr = cur_val; - set_math_char(get_math_code(cur_chr)); -} - -@ @c -static void run_math_char_num_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - if (cur_chr == 0) - mval = scan_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_mathchar(umath_mathcode); - else - mval = scan_mathchar(umathnum_mathcode); - set_math_char(mval); -} - -@ @c -static void run_math_given_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, tex_mathcode); - set_math_char(mval); -} - -static void run_xmath_given_mmode (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - mval = mathchar_from_integer(cur_chr, umath_mathcode); - set_math_char(mval); -} - -@ @c -static void run_delim_num (void) { - mathcodeval mval; /* to build up an argument to |set_math_char| */ - if (cur_chr == 0) - mval = scan_delimiter_as_mathchar(tex_mathcode); - else - mval = scan_delimiter_as_mathchar(umath_mathcode); - set_math_char(mval); - -} - -@ @c -static void run_vcenter (void) { - scan_spec(vcenter_group); - normal_paragraph(); - push_nest(); - mode = -vmode; - prev_depth_par = ignore_depth; - if (every_vbox_par != null) - begin_token_list(every_vbox_par, every_vbox_text); -} - -@ @c -static void run_math_style (void) { - tail_append(new_style((small_number) cur_chr)); -} - -@ @c -static void run_non_script (void) { - tail_append(new_glue(zero_glue)); - subtype(tail) = cond_math_glue; -} - -@ @c -static void run_math_choice (void) { - if (cur_chr == 0) - append_choices(); - else - setup_math_style(); -} - -@ @c -static void run_math_shift (void) { - if (cur_group == math_shift_group) - after_math(); - else - off_save(); -} - -@ @c -static void run_after_assignment (void) { - get_token(); - after_token = cur_tok; -} - -@ @c -static void run_after_group (void) { - get_token(); - save_for_after(cur_tok); -} - -@ @c -static void run_extension (void) { - do_extension(0); -} - -static void run_normal (void) { -{ - switch (cur_chr) { - case save_pos_code: - new_whatsit(save_pos_node); - break; - case save_cat_code_table_code: - scan_int(); - if ((cur_val < 0) || (cur_val > 0x7FFF)) { - print_err("Invalid \\catcode table"); - help1("All \\catcode table ids must be between 0 and 0x7FFF"); - error(); - } else { - if (cur_val == cat_code_table_par) { - print_err("Invalid \\catcode table"); - help1("You cannot overwrite the current \\catcode table"); - error(); - } else { - copy_cat_codes(cat_code_table_par, cur_val); - } - } - break; - case init_cat_code_table_code: - scan_int(); - if ((cur_val < 0) || (cur_val > 0x7FFF)) { - print_err("Invalid \\catcode table"); - help1("All \\catcode table ids must be between 0 and 0x7FFF"); - error(); - } else { - if (cur_val == cat_code_table_par) { - print_err("Invalid \\catcode table"); - help1("You cannot overwrite the current \\catcode table"); - error(); - } else { - initex_cat_codes(cur_val); - } - } - break; - case set_random_seed_code: - /* Negative random seed values are silently converted to positive ones */ - scan_int(); - if (cur_val < 0) - negate(cur_val); - random_seed = cur_val; - init_randoms(random_seed); - break; - case late_lua_code: - new_whatsit(late_lua_node); /* type == normal */ - late_lua_name(tail) = scan_lua_state(); - (void) scan_toks(false, false); - late_lua_data(tail) = def_ref; - break; - case expand_font_code: - read_expand_font(); - break; - default: - confusion("int1"); - break; - } - } -} - -/* - this is experimental and not used for production, only for testing and writing - macros (some options stay) - -*/ - -#define mathoption_set_int(A) \ - scan_int(); \ - word_define(mathoption_int_base+A, cur_val); - -static void run_option(void) { - int a = 0 ; - switch (cur_chr) { - case math_option_code: - if (scan_keyword("old")) { - mathoption_set_int(c_mathoption_old_code); - } else if (scan_keyword("noitaliccompensation")) { - mathoption_set_int(c_mathoption_no_italic_compensation_code); - } else if (scan_keyword("nocharitalic")) { - mathoption_set_int(c_mathoption_no_char_italic_code); - } else if (scan_keyword("useoldfractionscaling")) { - mathoption_set_int(c_mathoption_use_old_fraction_scaling_code); - } else if (scan_keyword("umathcodemeaning")) { - mathoption_set_int(c_mathoption_umathcode_meaning_code); - } else { - normal_warning("mathoption","unknown key"); - } - break; - default: - /* harmless */ - break; - } -} - -@ For mode-independent commands, the following macro is useful. - -Also, there is a list of cases where the user has probably gotten into or out of math -mode by mistake. \TeX\ will insert a dollar sign and rescan the current token, and -it makes sense ot have a macro for that as well. - -@c -#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B -#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; - - -@ The |main_control| uses a jump table, and |init_main_control| sets that table up. -@c -typedef void (*main_control_function) (void); -main_control_function *jump_table; - -static void init_main_control (void) { - jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ; - - jump_table[hmode + char_num_cmd] = run_char_num; - jump_table[hmode + letter_cmd] = run_char; - jump_table[hmode + other_char_cmd] = run_char; - jump_table[hmode + char_given_cmd] = run_char; - jump_table[hmode + spacer_cmd] = run_app_space; - jump_table[hmode + ex_space_cmd] = run_app_space; - jump_table[mmode + ex_space_cmd] = run_app_space; - jump_table[hmode + boundary_cmd] = run_boundary; - jump_table[hmode + char_ghost_cmd] = run_char_ghost; - jump_table[mmode + char_ghost_cmd] = run_char_ghost; - any_mode(relax_cmd, run_relax); - jump_table[vmode + spacer_cmd] = run_relax; - jump_table[mmode + spacer_cmd] = run_relax; - jump_table[mmode + boundary_cmd] = run_relax; - any_mode(ignore_spaces_cmd,run_ignore_spaces); - jump_table[vmode + stop_cmd] = run_stop; - jump_table[vmode + math_char_num_cmd] = run_non_math_math; - jump_table[vmode + math_given_cmd] = run_non_math_math; - jump_table[vmode + xmath_given_cmd] = run_non_math_math; - jump_table[hmode + math_char_num_cmd] = run_math_char_num; - jump_table[hmode + math_given_cmd] = run_math_given; - jump_table[hmode + xmath_given_cmd] = run_xmath_given; - - jump_table[vmode + vmove_cmd] = report_illegal_case; - jump_table[hmode + hmove_cmd] = report_illegal_case; - jump_table[mmode + hmove_cmd] = report_illegal_case; - any_mode(last_item_cmd, report_illegal_case); - jump_table[vmode + vadjust_cmd] = report_illegal_case; - jump_table[vmode + ital_corr_cmd] = report_illegal_case; - non_math(eq_no_cmd,report_illegal_case); - any_mode(mac_param_cmd,report_illegal_case); - - non_math(sup_mark_cmd, insert_dollar_sign); - non_math(sub_mark_cmd, insert_dollar_sign); - non_math(super_sub_script_cmd, insert_dollar_sign); - non_math(math_comp_cmd, insert_dollar_sign); - non_math(delim_num_cmd, insert_dollar_sign); - non_math(left_right_cmd, insert_dollar_sign); - non_math(above_cmd, insert_dollar_sign); - non_math(radical_cmd, insert_dollar_sign); - non_math(math_style_cmd, insert_dollar_sign); - non_math(math_choice_cmd, insert_dollar_sign); - non_math(vcenter_cmd, insert_dollar_sign); - non_math(non_script_cmd, insert_dollar_sign); - non_math(mkern_cmd, insert_dollar_sign); - non_math(limit_switch_cmd, insert_dollar_sign); - non_math(mskip_cmd, insert_dollar_sign); - non_math(math_accent_cmd, insert_dollar_sign); - jump_table[mmode + endv_cmd] = insert_dollar_sign; - jump_table[mmode + par_end_cmd] = insert_dollar_sign_par_end; - jump_table[mmode + stop_cmd] = insert_dollar_sign; - jump_table[mmode + vskip_cmd] = insert_dollar_sign; - jump_table[mmode + un_vbox_cmd] = insert_dollar_sign; - jump_table[mmode + valign_cmd] = insert_dollar_sign; - jump_table[mmode + hrule_cmd] = insert_dollar_sign; - jump_table[mmode + no_hrule_cmd] = insert_dollar_sign; - jump_table[vmode + hrule_cmd] = run_rule; - jump_table[vmode + no_hrule_cmd] = run_rule; - jump_table[hmode + vrule_cmd] = run_rule; - jump_table[hmode + no_vrule_cmd] = run_rule; - jump_table[mmode + vrule_cmd] = run_rule; - jump_table[mmode + no_vrule_cmd] = run_rule; - jump_table[vmode + vskip_cmd] = append_glue; - jump_table[hmode + hskip_cmd] = append_glue; - jump_table[mmode + hskip_cmd] = append_glue; - jump_table[mmode + mskip_cmd] = append_glue; - any_mode(kern_cmd, append_kern); - jump_table[mmode + mkern_cmd] = append_kern; - non_math(left_brace_cmd, run_left_brace); - any_mode(begin_group_cmd,run_begin_group); - any_mode(end_group_cmd, run_end_group); - any_mode(right_brace_cmd, handle_right_brace); - jump_table[vmode + hmove_cmd] = run_move; - jump_table[hmode + vmove_cmd] = run_move; - jump_table[mmode + vmove_cmd] = run_move; - any_mode(leader_ship_cmd, run_leader_ship); - any_mode(make_box_cmd, run_make_box); - any_mode(assign_box_dir_cmd, run_box_dir); - jump_table[vmode + start_par_cmd] = run_start_par_vmode; - jump_table[hmode + start_par_cmd] = run_start_par; - jump_table[mmode + start_par_cmd] = run_start_par; - jump_table[vmode + letter_cmd] = run_new_graf; - jump_table[vmode + other_char_cmd] = run_new_graf; - jump_table[vmode + char_num_cmd] = run_new_graf; - jump_table[vmode + char_given_cmd] = run_new_graf; - jump_table[vmode + char_ghost_cmd] = run_new_graf; - jump_table[vmode + math_shift_cmd] = run_new_graf; - jump_table[vmode + math_shift_cs_cmd] = run_new_graf; - jump_table[vmode + un_hbox_cmd] = run_new_graf; - jump_table[vmode + vrule_cmd] = run_new_graf; - jump_table[vmode + no_vrule_cmd] = run_new_graf; - jump_table[vmode + accent_cmd] = run_new_graf; - jump_table[vmode + discretionary_cmd] = run_new_graf; - jump_table[vmode + hskip_cmd] = run_new_graf; - jump_table[vmode + valign_cmd] = run_new_graf; - jump_table[vmode + ex_space_cmd] = run_new_graf; - jump_table[vmode + boundary_cmd] = run_new_graf; - jump_table[vmode + par_end_cmd] = run_par_end_vmode; - jump_table[hmode + par_end_cmd] = run_par_end_hmode; - jump_table[hmode + stop_cmd] = head_for_vmode; - jump_table[hmode + vskip_cmd] = head_for_vmode; - jump_table[hmode + hrule_cmd] = head_for_vmode; - jump_table[hmode + no_hrule_cmd] = head_for_vmode; - jump_table[hmode + un_vbox_cmd] = head_for_vmode; - jump_table[hmode + halign_cmd] = head_for_vmode; - any_mode(insert_cmd,begin_insert_or_adjust); - jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust; - jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust; - any_mode(mark_cmd, handle_mark); - any_mode(break_penalty_cmd, append_penalty); - any_mode(remove_item_cmd, delete_last); - jump_table[vmode + un_vbox_cmd] = unpackage; - jump_table[hmode + un_hbox_cmd] = unpackage; - jump_table[mmode + un_hbox_cmd] = unpackage; - jump_table[hmode + ital_corr_cmd] = append_italic_correction; - jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode; - jump_table[hmode + discretionary_cmd] = append_discretionary; - jump_table[mmode + discretionary_cmd] = append_discretionary; - any_mode(assign_local_box_cmd, run_local_box); - jump_table[hmode + accent_cmd] = make_accent; - any_mode(car_ret_cmd,align_error); - any_mode(tab_mark_cmd,align_error); - any_mode(no_align_cmd,no_align_error); - any_mode(omit_cmd, omit_error); - jump_table[vmode + halign_cmd] = init_align; - jump_table[hmode + valign_cmd] = init_align; - jump_table[mmode + halign_cmd] = run_halign_mmode; - jump_table[vmode + endv_cmd] = do_endv; - jump_table[hmode + endv_cmd] = do_endv; - any_mode(end_cs_name_cmd, cs_error); - jump_table[hmode + math_shift_cmd] = init_math; - jump_table[hmode + math_shift_cs_cmd] = init_math; - jump_table[mmode + eq_no_cmd] = run_eq_no; - jump_table[mmode + left_brace_cmd] = math_left_brace; - jump_table[mmode + letter_cmd] = run_letter_mmode; - jump_table[mmode + other_char_cmd] = run_letter_mmode; - jump_table[mmode + char_given_cmd] = run_letter_mmode; - jump_table[mmode + char_num_cmd] = run_char_num_mmode; - jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode; - jump_table[mmode + math_given_cmd] = run_math_given_mmode; - jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode; - jump_table[mmode + delim_num_cmd] = run_delim_num; - jump_table[mmode + math_comp_cmd] = math_math_comp; - jump_table[mmode + limit_switch_cmd] = math_limit_switch; - jump_table[mmode + radical_cmd] = math_radical; - jump_table[mmode + accent_cmd] = math_ac; - jump_table[mmode + math_accent_cmd] = math_ac; - jump_table[mmode + vcenter_cmd] = run_vcenter; - jump_table[mmode + math_style_cmd] = run_math_style; - jump_table[mmode + non_script_cmd] = run_non_script; - jump_table[mmode + math_choice_cmd] = run_math_choice; - jump_table[mmode + above_cmd] = math_fraction; - jump_table[mmode + sub_mark_cmd] = sub_sup; - jump_table[mmode + sup_mark_cmd] = sub_sup; - jump_table[mmode + super_sub_script_cmd] = sub_sup; - jump_table[mmode + left_right_cmd] = math_left_right; - jump_table[mmode + math_shift_cmd] = run_math_shift; - jump_table[mmode + math_shift_cs_cmd] = run_math_shift; - any_mode(toks_register_cmd, prefixed_command); - any_mode(assign_toks_cmd, prefixed_command); - any_mode(assign_int_cmd, prefixed_command); - any_mode(assign_attr_cmd, prefixed_command); - any_mode(assign_dir_cmd, prefixed_command); - any_mode(assign_dimen_cmd, prefixed_command); - any_mode(assign_glue_cmd, prefixed_command); - any_mode(assign_mu_glue_cmd, prefixed_command); - any_mode(assign_font_dimen_cmd, prefixed_command); - any_mode(assign_font_int_cmd, prefixed_command); - any_mode(set_aux_cmd, prefixed_command); - any_mode(set_prev_graf_cmd, prefixed_command); - any_mode(set_page_dimen_cmd, prefixed_command); - any_mode(set_page_int_cmd, prefixed_command); - any_mode(set_box_dimen_cmd, prefixed_command); - any_mode(set_tex_shape_cmd, prefixed_command); - any_mode(set_etex_shape_cmd, prefixed_command); - any_mode(def_char_code_cmd, prefixed_command); - any_mode(def_del_code_cmd, prefixed_command); - any_mode(extdef_math_code_cmd, prefixed_command); - any_mode(extdef_del_code_cmd, prefixed_command); - any_mode(def_family_cmd, prefixed_command); - any_mode(set_math_param_cmd, prefixed_command); - any_mode(set_font_cmd, prefixed_command); - any_mode(def_font_cmd, prefixed_command); - any_mode(letterspace_font_cmd, prefixed_command); - any_mode(copy_font_cmd, prefixed_command); - any_mode(set_font_id_cmd, prefixed_command); - any_mode(register_cmd, prefixed_command); - any_mode(advance_cmd, prefixed_command); - any_mode(multiply_cmd, prefixed_command); - any_mode(divide_cmd, prefixed_command); - any_mode(prefix_cmd, prefixed_command); - any_mode(let_cmd, prefixed_command); - any_mode(shorthand_def_cmd, prefixed_command); - any_mode(read_to_cs_cmd, prefixed_command); - any_mode(def_cmd, prefixed_command); - any_mode(set_box_cmd, prefixed_command); - any_mode(hyph_data_cmd, prefixed_command); - any_mode(set_interaction_cmd, prefixed_command); - any_mode(after_assignment_cmd,run_after_assignment); - any_mode(after_group_cmd,run_after_group); - any_mode(in_stream_cmd,open_or_close_in); - any_mode(message_cmd,issue_message); - any_mode(case_shift_cmd, shift_case); - any_mode(xray_cmd, show_whatever); - any_mode(normal_cmd, run_normal); - any_mode(extension_cmd, run_extension); - any_mode(option_cmd, run_option); -} - -@ And here is |main_control| itself. It is quite short nowadays. - -@c -void main_control(void) -{ - main_control_state = goto_next; - init_main_control () ; - - if (equiv(every_job_loc) != null) - begin_token_list(equiv(every_job_loc), every_job_text); - - while (1) { - if (main_control_state == goto_skip_token) - main_control_state = goto_next; /* reset */ - else - get_x_token(); - - /* Give diagnostic information, if requested */ - /* When a new token has just been fetched at |big_switch|, we have an - ideal place to monitor \TeX's activity. */ - if (interrupt != 0 && OK_to_interrupt) { - back_input(); - check_interrupt(); - continue; - } - if (tracing_commands_par > 0) - show_cur_cmd_chr(); - - (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */ - - if (main_control_state == goto_return) { - return; - } - } - return; /* not reached */ -} - -@ @c -void app_space(void) -{ /* handle spaces when |space_factor<>1000| */ - halfword q; /* glue node */ - if ((space_factor_par >= 2000) && (! glue_is_zero(xspace_skip_par))) { - q = new_param_glue(xspace_skip_code); - /* so from now we have a subtype with spaces: */ - subtype(q) = xspace_skip_code + 1; - } else { - if (!glue_is_zero(space_skip_par)) { - q = new_glue(space_skip_par); - } else { - q = new_glue(zero_glue); - width(q) = space(cur_font_par); - stretch(q) = space_stretch(cur_font_par); - shrink(q) = space_shrink(cur_font_par); - } - /* Modify the glue specification in |q| according to the space factor */ - if (space_factor_par >= 2000) - width(q) = width(q) + extra_space(cur_font_par); - stretch(q) = xn_over_d(stretch(q), space_factor_par, 1000); - shrink(q) = xn_over_d(shrink(q), 1000, space_factor_par); - - /* so from now we have a subtype with spaces: */ - subtype(q) = space_skip_code + 1; - } - couple_nodes(tail, q); - tail = q; -} - -@ @c -void insert_dollar_sign(void) -{ - back_input(); - cur_tok = math_shift_token + '$'; - print_err("Missing $ inserted"); - help2("I've inserted a begin-math/end-math symbol since I think", - "you left one out. Proceed, with fingers crossed."); - ins_error(); -} - -@ We can silently ignore \.{\\par}s in a math formula. - -@c -void insert_dollar_sign_par_end(void) -{ - if (!suppress_mathpar_error_par) { - insert_dollar_sign() ; - } -} - -@ The `|you_cant|' procedure prints a line saying that the current command -is illegal in the current mode; it identifies these things symbolically. - -@c -void you_cant(void) -{ - print_err("You can't use `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_in_mode(mode); -} - -@ -When erroneous situations arise, \TeX\ usually issues an error message -specific to the particular error. For example, `\.{\\noalign}' should -not appear in any mode, since it is recognized by the |align_peek| routine -in all of its legitimate appearances; a special error message is given -when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate -error message is simply that the user is not allowed to do what he or she -has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode, -and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated -here and in the other sections referred to under `See also \dots.' - -@c -void report_illegal_case(void) -{ - you_cant(); - help4("Sorry, but I'm not programmed to handle this case;", - "I'll just pretend that you didn''t ask for it.", - "If you're in the wrong mode, you might be able to", - "return to the right one by typing `I}' or `I$' or `I\\par'."); - error(); -} - -@ Some operations are allowed only in privileged modes, i.e., in cases -that |mode>0|. The |privileged| function is used to detect violations -of this rule; it issues an error message and returns |false| if the -current |mode| is negative. - -@c -boolean privileged(void) -{ - if (mode > 0) { - return true; - } else { - report_illegal_case(); - return false; - } -} - -@ We don't want to leave |main_control| immediately when a |stop| command -is sensed, because it may be necessary to invoke an \.{\\output} routine -several times before things really grind to a halt. (The output routine -might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.) -Therefore |its_all_over| is |true| only when the current page -and contribution list are empty, and when the last output was not a -``dead cycle.'' - -@c -boolean its_all_over(void) -{ /* do this when \.{\\end} or \.{\\dump} occurs */ - if (privileged()) { - if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) { - return true; - } - back_input(); /* we will try to end again after ejecting residual material */ - tail_append(new_null_box()); - width(tail) = hsize_par; - tail_append(new_glue(fill_glue)); - tail_append(new_penalty(-010000000000)); - normal_page_filter(end); - build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */ - } - return false; -} - - -@ The |hskip| and |vskip| command codes are used for control sequences -like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. -The difference is in the value of |cur_chr|. - -All the work relating to glue creation has been relegated to the -following subroutine. It does not call |build_page|, because it is -used in at least one place where that would be a mistake. - -@c -void append_glue(void) -{ - int s = cur_chr; - switch (s) { - case fil_code: - cur_val = new_glue(fil_glue); - break; - case fill_code: - cur_val = new_glue(fill_glue); - break; - case ss_code: - cur_val = new_glue(ss_glue); - break; - case fil_neg_code: - cur_val = new_glue(fil_neg_glue); - break; - case skip_code: - scan_glue(glue_val_level); - break; - case mskip_code: - scan_glue(mu_val_level); - break; - } - /* now |cur_val| points to the glue specification */ - tail_append(new_glue(cur_val)); - flush_node(cur_val); - if (s > skip_code) { - subtype(tail) = mu_glue; - } -} - -@ @c -void append_kern(void) -{ - int s; /* |subtype| of the kern node */ - s = cur_chr; - scan_dimen((s == mu_glue), false, false); - tail_append(new_kern(cur_val)); - subtype(tail) = (quarterword) s; -} - -@ We have to deal with errors in which braces and such things are not -properly nested. Sometimes the user makes an error of commission by -inserting an extra symbol, but sometimes the user makes an error of omission. -\TeX\ can't always tell one from the other, so it makes a guess and tries -to avoid getting into a loop. - -The |off_save| routine is called when the current group code is wrong. It tries -to insert something into the user's input that will help clean off -the top level. - -@c -void off_save(void) -{ - halfword p, q; /* inserted token */ - if (cur_group == bottom_level) { - /* Drop current token and complain that it was unmatched */ - print_err("Extra "); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - help1("Things are pretty mixed up, but I think the worst is over."); - error(); - - } else { - back_input(); - p = get_avail(); - set_token_link(temp_token_head, p); - print_err("Missing "); - /* Prepare to insert a token that matches |cur_group|, and print what it is */ - /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */ - switch (cur_group) { - case semi_simple_group: - set_token_info(p, cs_token_flag + frozen_end_group); - tprint_esc("endgroup"); - break; - case math_shift_group: - set_token_info(p, math_shift_token + '$'); - print_char('$'); - break; - case math_left_group: - set_token_info(p, cs_token_flag + frozen_right); - q = get_avail(); - set_token_link(p, q); - p = token_link(p); - set_token_info(p, other_token + '.'); - tprint_esc("right."); - break; - default: - set_token_info(p, right_brace_token + '}'); - print_char('}'); - break; - } - tprint(" inserted"); - ins_list(token_link(temp_token_head)); - help5("I've inserted something that you may have forgotten.", - "(See the above.)", - "With luck, this will get me unwedged. But if you", - "really didn't forget anything, try typing `2' now; then", - "my insertion and my current dilemma will both disappear."); - error(); - } -} - -@ The routine for a |right_brace| character branches into many subcases, -since a variety of things may happen, depending on |cur_group|. Some -types of groups are not supposed to be ended by a right brace; error -messages are given in hopes of pinpointing the problem. Most branches -of this routine will be filled in later, when we are ready to understand -them; meanwhile, we must prepare ourselves to deal with such errors. - -@c -void handle_right_brace(void) -{ - halfword p, q; /* for short-term use */ - scaled d; /* holds |split_max_depth| in |insert_group| */ - int f; /* holds |floating_penalty| in |insert_group| */ - p = null; - switch (cur_group) { - case simple_group: - fixup_directions(); - break; - case bottom_level: - print_err("Too many }'s"); - help2("You've closed more groups than you opened.", - "Such booboos are generally harmless, so keep going."); - error(); - break; - case semi_simple_group: - case math_shift_group: - case math_left_group: - extra_right_brace(); - break; - case hbox_group: - /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or - \.{\\vtop} construction, the |package| routine comes into action. We might - also have to finish a paragraph that hasn't ended. */ - package(0); - break; - case adjusted_hbox_group: - adjust_tail = adjust_head; - pre_adjust_tail = pre_adjust_head; - package(0); - break; - case vbox_group: - end_graf(vbox_group); - package(0); - break; - case vtop_group: - end_graf(vtop_group); - package(vtop_code); - break; - case insert_group: - end_graf(insert_group); - q = new_glue(split_top_skip_par); - d = split_max_depth_par; - f = floating_penalty_par; - unsave(); - save_ptr--; - /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */ - p = vpack(vlink(head), 0, additional, -1); - pop_nest(); - if (saved_type(0) == saved_insert) { - tail_append(new_node(ins_node, saved_value(0))); - height(tail) = height(p) + depth(p); - ins_ptr(tail) = list_ptr(p); - split_top_ptr(tail) = q; - depth(tail) = d; - float_cost(tail) = f; - } else if (saved_type(0) == saved_adjust) { - tail_append(new_node(adjust_node, saved_value(0))); - adjust_ptr(tail) = list_ptr(p); - flush_node(q); - } else { - confusion("insert_group"); - } - list_ptr(p) = null; - flush_node(p); - if (nest_ptr == 0) { - checked_page_filter(insert); - build_page(); - } - break; - case output_group: - /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */ - if (dir_level(text_dir_ptr) == cur_level) { - /* DIR: Remove from |text_dir_ptr| */ - halfword text_dir_tmp = vlink(text_dir_ptr); - flush_node(text_dir_ptr); - text_dir_ptr = text_dir_tmp; - } - resume_after_output(); - break; - case disc_group: - build_discretionary(); - break; - case local_box_group: - build_local_box(); - break; - case align_group: - back_input(); - cur_tok = cs_token_flag + frozen_cr; - print_err("Missing \\cr inserted"); - help1("I'm guessing that you meant to end an alignment here."); - ins_error(); - break; - case no_align_group: - end_graf(no_align_group); - unsave(); - align_peek(); - break; - case vcenter_group: - end_graf(vcenter_group); - finish_vcenter(); - break; - case math_choice_group: - build_choices(); - break; - case math_group: - close_math_group(p); - break; - default: - confusion("rightbrace"); - break; - } -} - -@ @c -void extra_right_brace(void) -{ - print_err("Extra }, or forgotten "); - switch (cur_group) { - case semi_simple_group: - tprint_esc("endgroup"); - break; - case math_shift_group: - print_char('$'); - break; - case math_left_group: - tprint_esc("right"); - break; - } - help5("I've deleted a group-closing symbol because it seems to be", - "spurious, as in `$x}$'. But perhaps the } is legitimate and", - "you forgot something else, as in `\\hbox{$x}'. In such cases", - "the way to recover is to insert both the forgotten and the", - "deleted material, e.g., by typing `I$}'."); - error(); - incr(align_state); -} - -@ Here is where we clear the parameters that are supposed to revert to their -default values after every paragraph and when internal vertical mode is entered. - -@c -void normal_paragraph(void) -{ - if (looseness_par != 0) - eq_word_define(int_base + looseness_code, 0); - if (hang_indent_par != 0) - eq_word_define(dimen_base + hang_indent_code, 0); - if (hang_after_par != 1) - eq_word_define(int_base + hang_after_code, 1); - if (par_shape_par_ptr != null) - eq_define(par_shape_loc, shape_ref_cmd, null); - if (inter_line_penalties_par_ptr != null) - eq_define(inter_line_penalties_loc, shape_ref_cmd, null); - if (shape_mode_par > 0) - eq_word_define(dimen_base + shape_mode_code, 0); -} - -@ The global variable |cur_box| will point to a newly-made box. If the box -is void, we will have |cur_box=null|. Otherwise we will have -|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node| -case can occur only with leaders. - -@c -halfword cur_box; /* box to be placed into its context */ - -@ The |box_end| procedure does the right thing with |cur_box|, if -|box_context| represents the context as explained above. - -@c -void box_end(int box_context) -{ - if (box_context < box_flag) { - /* Append box |cur_box| to the current list, shifted by |box_context| */ - /* - The global variable |adjust_tail| will be non-null if and only if the - current box might include adjustments that should be appended to the - current vertical list. - */ - if (cur_box != null) { - shift_amount(cur_box) = box_context; - if (abs(mode) == vmode) { - if (pre_adjust_tail != null) { - if (pre_adjust_head != pre_adjust_tail) - append_list(pre_adjust_head, pre_adjust_tail); - pre_adjust_tail = null; - } - append_to_vlist(cur_box,lua_key_index(box)); - if (adjust_tail != null) { - if (adjust_head != adjust_tail) - append_list(adjust_head, adjust_tail); - adjust_tail = null; - } - if (mode > 0) { - checked_page_filter(box); - build_page(); - } - } else { - if (abs(mode) == hmode) - space_factor_par = 1000; - else - cur_box = new_sub_box(cur_box); - couple_nodes(tail, cur_box); - tail = cur_box; - } - } - } else if (box_context < ship_out_flag) { - /* Store |cur_box| in a box register */ - if (box_context < global_box_flag) - eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box); - else - geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box); - } else if (cur_box != null) { - if (box_context > ship_out_flag) { - /* Append a new leader node that uses |cur_box| */ - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) || - ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) { - append_glue(); - subtype(tail) = (quarterword) (box_context - (leader_flag - a_leaders)); - leader_ptr(tail) = cur_box; - } else { - print_err("Leaders not followed by proper glue"); - help3 - ("You should say `\\leaders '.", - "I found the , but there's no suitable", - ", so I'm ignoring these leaders."); - back_error(); - flush_node_list(cur_box); - } - } else { - ship_out(static_pdf, cur_box, SHIPPING_PAGE); - } - } -} - -@ the next input should specify a box or perhaps a rule - -@c -void scan_box(int box_context) -{ - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if (cur_cmd == make_box_cmd) { - begin_box(box_context); - } else if ((box_context >= leader_flag) && - ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd) || - (cur_cmd == no_hrule_cmd) || (cur_cmd == no_vrule_cmd))) { - cur_box = scan_rule_spec(); - box_end(box_context); - } else { - print_err("A was supposed to be here"); - help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or", - "something like that. So you might find something missing in", - "your output. But keep trying; you can fix this later."); - back_error(); - } -} - -@ @c -void new_graf(boolean indented) -{ - halfword p, q, dir_graf_tmp; - halfword dir_rover; - prev_graf_par = 0; - if ((mode == vmode) || (head != tail)) { - tail_append(new_param_glue(par_skip_code)); - } - push_nest(); - mode = hmode; - space_factor_par = 1000; - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(new_graf_par_code)); - if (indented) { - p = new_null_box(); - box_dir(p) = par_direction_par; - width(p) = par_indent_par; - subtype(p) = indent_list; - q = tail; - tail_append(p); - } else { - q = tail; - } - dir_rover = text_dir_ptr; - while (dir_rover != null) { - if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction_par)) { - dir_graf_tmp = new_dir(dir_dir(dir_rover)); - try_couple_nodes(dir_graf_tmp,vlink(q)); - couple_nodes(q,dir_graf_tmp); - } - dir_rover = vlink(dir_rover); - } - q = head; - while (vlink(q) != null) - q = vlink(q); - tail = q; - if (every_par_par != null) - begin_token_list(every_par_par, every_par_text); - if (nest_ptr == 1) { - checked_page_filter(new_graf); - build_page(); /* put |par_skip| glue on current page */ - } -} - -@ @c -void indent_in_hmode(void) -{ - halfword p; - if (cur_chr > 0) { /* \.{\\indent} */ - p = new_null_box(); - width(p) = par_indent_par; - if (abs(mode) == hmode) - space_factor_par = 1000; - else - p = new_sub_box(p); - tail_append(p); - } -} - -@ @c -void head_for_vmode(void) -{ - if (mode < 0) { - if ((cur_cmd != hrule_cmd) && (cur_cmd != no_hrule_cmd)) { - off_save(); - } else { - print_err("You can't use `\\hrule' here except with leaders"); - help2("To put a horizontal rule in an hbox or an alignment,", - "you should use \\leaders or \\hrulefill (see The TeXbook)."); - error(); - } - } else { - back_input(); - cur_tok = par_token; - back_input(); - token_type = inserted; - } -} - -@ TODO (BUG?): |dir_save| would have been set by |line_break| by means -of |post_line_break|, but this is not done right now, as it introduces -pretty heavy memory leaks. This means the current code is probably -wrong in some way that relates to in-paragraph displays. - -@c -void end_graf(int line_break_context) -{ - if (mode == hmode) { - if ((head == tail) || (vlink(head) == tail)) { - if (vlink(head) == tail) - flush_node(vlink(head)); - pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */ - } else { - line_break(false, line_break_context); - } - if (dir_save != null) { - flush_node_list(dir_save); - dir_save = null; - } - normal_paragraph(); - error_count = 0; - } -} - -@ @c -void begin_insert_or_adjust(void) -{ - if (cur_cmd != vadjust_cmd) { - scan_register_num(); - if (cur_val == output_box_par) { - print_err("You can't \\insert"); - print_int(output_box_par); - help1("I'm changing to \\insert0; box \\outputbox is special."); - error(); - cur_val = 0; - } - set_saved_record(0, saved_insert, 0, cur_val); - } else if (scan_keyword("pre")) { - set_saved_record(0, saved_adjust, 0, 1); - } else { - set_saved_record(0, saved_adjust, 0, 0); - } - save_ptr++; - new_save_level(insert_group); - scan_left_brace(); - normal_paragraph(); - push_nest(); - mode = -vmode; - prev_depth_par = ignore_depth; -} - -@ I (TH)'ve renamed the |make_mark| procedure to this, because if the -current chr code is 1, then the actual command was \.{\\clearmarks}, -which does not generate a mark node but instead destroys the current -mark tokenlists. - -@c -void handle_mark(void) -{ - halfword p; /* new node */ - halfword c; /* the mark class */ - if (cur_chr == clear_marks_code) { - scan_mark_num(); - c = cur_val; - delete_top_mark(c); - delete_bot_mark(c); - delete_first_mark(c); - delete_split_first_mark(c); - delete_split_bot_mark(c); - } else { - if (cur_chr == 0) { - c = 0; - } else { - scan_mark_num(); - c = cur_val; - if (c > biggest_used_mark) - biggest_used_mark = c; - } - p = scan_toks(false, true); - p = new_node(mark_node, 0); /* the |subtype| is not used */ - mark_class(p) = c; - mark_ptr(p) = def_ref; - couple_nodes(tail, p); - tail = p; - } -} - -@ @c -void append_penalty(void) -{ - scan_int(); - tail_append(new_penalty(cur_val)); - if (mode == vmode) { - checked_page_filter(penalty); - build_page(); - } -} - -@ When |delete_last| is called, |cur_chr| is the |type| of node that -will be deleted, if present. - -The |remove_item| command removes a penalty, kern, or glue node if it -appears at the tail of the current list, using a brute-force linear scan. -Like \.{\\lastbox}, this command is not allowed in vertical mode (except -internal vertical mode), since the current list in vertical mode is sent -to the page builder. But if we happen to be able to implement it in -vertical mode, we do. - -@c -void delete_last(void) -{ - halfword p, q; /* run through the current list */ - if ((mode == vmode) && (tail == head)) { - /* Apologize for inability to do the operation now, - unless \.{\\unskip} follows non-glue */ - if ((cur_chr != glue_node) || (last_glue != max_halfword)) { - you_cant(); - if (cur_chr == kern_node) { - help2 - ("Sorry...I usually can't take things from the current page.", - "Try `I\\kern-\\lastkern' instead."); - } else if (cur_chr != glue_node) { - help2 - ("Sorry...I usually can't take things from the current page.", - "Perhaps you can make the output routine do it."); - } else { - help2 - ("Sorry...I usually can't take things from the current page.", - "Try `I\\vskip-\\lastskip' instead."); - } - error(); - } - } else { - /* todo: clean this up */ - if (!is_char_node(tail)) { - if (type(tail) == cur_chr) { - q = head; - do { - p = q; - if (!is_char_node(q)) { - if (type(q) == disc_node) { - if (p == tail) - return; - } - } - q = vlink(p); - } while (q != tail); - vlink(p) = null; - flush_node_list(tail); - tail = p; - } - } - } -} - -@ @c -void unpackage(void) -{ - halfword p; /* the box */ - halfword r; /* to remove marginal kern nodes */ - int c; /* should we copy? */ - halfword s; /* for varmem assignment */ - if (cur_chr > copy_code) { - /* Handle saved items and |goto done| */ - try_couple_nodes(tail, disc_ptr[cur_chr]); - disc_ptr[cur_chr] = null; - goto DONE; - } - c = cur_chr; - scan_register_num(); - p = box(cur_val); - if (p == null) - return; - if ((abs(mode) == mmode) - || ((abs(mode) == vmode) && (type(p) != vlist_node)) - || ((abs(mode) == hmode) && (type(p) != hlist_node))) { - print_err("Incompatible list can't be unboxed"); - help3("Sorry, Pandora. (You sneaky devil.)", - "I refuse to unbox an \\hbox in vertical mode or vice versa.", - "And I can't open any boxes in math mode."); - error(); - return; - } - if (c == copy_code) { - s = copy_node_list(list_ptr(p)); - try_couple_nodes(tail,s); - } else { - try_couple_nodes(tail,list_ptr(p)); - box(cur_val) = null; - list_ptr(p) = null; - flush_node(p); - } - DONE: - while (vlink(tail) != null) { - r = vlink(tail); - if (!is_char_node(r) && (type(r) == margin_kern_node)) { - try_couple_nodes(tail,vlink(r)); - flush_node(r); - } - tail = vlink(tail); - } -} - -@ -Italic corrections are converted to kern nodes when the |ital_corr| command -follows a character. In math mode the same effect is achieved by appending -a kern of zero here, since italic corrections are supplied later. - -@c -void append_italic_correction(void) -{ - halfword p; /* |char_node| at the tail of the current list */ - internal_font_number f; /* the font in the |char_node| */ - if (tail != head) { - if (is_char_node(tail)) - p = tail; - else - return; - f = font(p); - tail_append(new_kern(char_italic(f, character(p)))); - subtype(tail) = italic_kern; - } -} - -@ @c -void append_local_box(int kind) -{ - incr(save_ptr); - set_saved_record(-1, saved_boxtype, 0, kind); - new_save_level(local_box_group); - scan_left_brace(); - push_nest(); - mode = -hmode; - space_factor_par = 1000; -} - -@ Discretionary nodes are easy in the common case `\.{\\-}', but in the -general case we must process three braces full of items. - -The space factor does not change when we append a discretionary node, -but it starts out as 1000 in the subsidiary lists. - -@c -void append_discretionary(void) -{ - int c; - tail_append(new_disc()); - subtype(tail) = (quarterword) cur_chr; - if (cur_chr == explicit_disc) { - /* \- */ - c = get_pre_hyphen_char(cur_lang_par); - if (c != 0) { - vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); - alink(vlink(pre_break(tail))) = pre_break(tail); - tlink(pre_break(tail)) = vlink(pre_break(tail)); - } - c = get_post_hyphen_char(cur_lang_par); - if (c != 0) { - vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c); - alink(vlink(post_break(tail))) = post_break(tail); - tlink(post_break(tail)) = vlink(post_break(tail)); - } - disc_penalty(tail) = ex_hyphen_penalty_par; - } else { - /* \discretionary */ - if (scan_keyword("penalty")) { - scan_int(); - disc_penalty(tail) = cur_val; - } - incr(save_ptr); - set_saved_record(-1, saved_disc, 0, 0); - new_save_level(disc_group); - scan_left_brace(); - push_nest(); - mode = -hmode; - space_factor_par = 1000; - /* already preset: disc_penalty(tail) = hyphen_penalty_par; */ - } -} - -@ The test for |p != null| ensures that empty \.{\\localleftbox} and - \.{\\localrightbox} commands are not applied. - -@c -void build_local_box(void) -{ - halfword p; - int kind; - unsave(); - assert(saved_type(-1) == saved_boxtype); - kind = saved_value(-1); - decr(save_ptr); - p = vlink(head); - pop_nest(); - if (p != null) - p = hpack(p, 0, additional, -1); - if (kind == 0) - eq_define(local_left_box_base, box_ref_cmd, p); - else - eq_define(local_right_box_base, box_ref_cmd, p); - if (abs(mode) == hmode) { - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(local_box_par_code)); - } - eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); -} - -@ The three discretionary lists are constructed somewhat as if they were -hboxes. A~subroutine called |build_discretionary| handles the transitions. -(This is sort of fun.) - -@c -void build_discretionary(void) -{ - halfword p, q; /* for link manipulation */ - int n; /* length of discretionary list */ - unsave(); - /* Prune the current list, if necessary, until it contains only - |char_node|, |kern_node|, |hlist_node|, |vlist_node| and - |rule_node| items; set |n| to the length of the list, - and set |q| to the lists tail */ - /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */ - q = head; - p = vlink(q); - n = 0; - while (p != null) { - if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) { - print_err("Improper discretionary list"); - help1("Discretionary lists must contain only boxes and kerns."); - error(); - begin_diagnostic(); - tprint_nl("The following discretionary sublist has been deleted:"); - show_box(p); - end_diagnostic(true); - flush_node_list(p); - vlink(q) = null; - break; - } - alink(p) = q; - q = p; - p = vlink(q); - incr(n); - } - - p = vlink(head); - pop_nest(); - assert(saved_type(-1) == saved_disc); - switch (saved_value(-1)) { - case 0: - if (n > 0) { - vlink(pre_break(tail)) = p; - alink(p) = pre_break(tail); - tlink(pre_break(tail)) = q; - } - break; - case 1: - if (n > 0) { - vlink(post_break(tail)) = p; - alink(p) = post_break(tail); - tlink(post_break(tail)) = q; - } - break; - case 2: - /* Attach list |p| to the current list, and record its length; - then finish up and |return| */ - if ((n > 0) && (abs(mode) == mmode)) { - print_err("Illegal math \\discretionary"); - help2("Sorry: The third part of a discretionary break must be", - "empty, in math formulas. I had to delete your third part."); - flush_node_list(p); - error(); - } else { - if (n > 0) { - vlink(no_break(tail)) = p; - alink(p) = no_break(tail); - tlink(no_break(tail)) = q; - } - } - decr(save_ptr); - return; - break; - } /* there are no other cases */ - set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1)); - new_save_level(disc_group); - scan_left_brace(); - push_nest(); - mode = -hmode; - space_factor_par = 1000; -} - -@ The positioning of accents is straightforward but tedious. Given an accent -of width |a|, designed for characters of height |x| and slant |s|; -and given a character of width |w|, height |h|, and slant |t|: We will shift -the accent down by |x-h|, and we will insert kern nodes that have the effect of -centering the accent over the character and shifting the accent to the -right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is -absent from the font, we will simply use the other, without shifting. - -@c -void make_accent(void) -{ - double s, t; /* amount of slant */ - halfword p, q, r; /* character, box, and kern nodes */ - internal_font_number f; /* relevant font */ - scaled a, h, x, w, delta; /* heights and widths, as explained above */ - scan_char_num(); - f = equiv(cur_font_loc); - p = new_glyph(f, cur_val); - if (p != null) { - x = x_height(f); - s = float_cast(slant(f)) / float_constant(65536); /* real division */ - a = glyph_width(p); - do_assignments(); - /* Create a character node |q| for the next character, - but set |q:=null| if problems arise */ - q = null; - f = equiv(cur_font_loc); - if ((cur_cmd == letter_cmd) || - (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) { - q = new_glyph(f, cur_chr); - } else if (cur_cmd == char_num_cmd) { - scan_char_num(); - q = new_glyph(f, cur_val); - } else { - back_input(); - } - - if (q != null) { - /* Append the accent with appropriate kerns, then set |p:=q| */ - /* The kern nodes appended here must be distinguished from other kerns, lest - they be wiped away by the hyphenation algorithm or by a previous line break. - - The two kerns are computed with (machine-dependent) |real| arithmetic, but - their sum is machine-independent; the net effect is machine-independent, - because the user cannot remove these nodes nor access them via \.{\\lastkern}. - */ - t = float_cast(slant(f)) / float_constant(65536); /* real division */ - w = glyph_width(q); - h = glyph_height(q); - if (h != x) { /* the accent must be shifted up or down */ - p = hpack(p, 0, additional, -1); - shift_amount(p) = x - h; - } - delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */ - r = new_kern(delta); - subtype(r) = accent_kern; - couple_nodes(tail, r); - couple_nodes(r, p); - tail = new_kern(-a - delta); - subtype(tail) = accent_kern; - couple_nodes(p, tail); - p = q; - - } - couple_nodes(tail, p); - tail = p; - space_factor_par = 1000; - } -} - -@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner -into |main_control|, it might be that the user has foolishly inserted -one of them into something that has nothing to do with alignment. But it is -far more likely that a left brace or right brace has been omitted, since -|get_next| takes actions appropriate to alignment only when `\.{\\cr}' -or `\.{\\span}' or tab marks occur with |align_state=0|. The following -program attempts to make an appropriate recovery. - -@c -void align_error(void) -{ - if (abs(align_state) > 2) { - /* Express consternation over the fact that no alignment is in progress */ - print_err("Misplaced "); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - if (cur_tok == tab_token + '&') { - help6("I can't figure out why you would want to use a tab mark", - "here. If you just want an ampersand, the remedy is", - "simple: Just type `I\\&' now. But if some right brace", - "up above has ended a previous alignment prematurely,", - "you're probably due for more error messages, and you", - "might try typing `S' now just to see what is salvageable."); - } else { - help5("I can't figure out why you would want to use a tab mark", - "or \\cr or \\span just now. If something like a right brace", - "up above has ended a previous alignment prematurely,", - "you're probably due for more error messages, and you", - "might try typing `S' now just to see what is salvageable."); - } - error(); - - } else { - back_input(); - if (align_state < 0) { - print_err("Missing { inserted"); - incr(align_state); - cur_tok = left_brace_token + '{'; - } else { - print_err("Missing } inserted"); - decr(align_state); - cur_tok = right_brace_token + '}'; - } - help3("I've put in what seems to be necessary to fix", - "the current column of the current alignment.", - "Try to go on, since this might almost work."); - ins_error(); - } -} - -@ The help messages here contain a little white lie, since \.{\\noalign} -and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. - -@c -void no_align_error(void) -{ - print_err("Misplaced \\noalign"); - help2("I expect to see \\noalign only after the \\cr of", - "an alignment. Proceed, and I'll ignore this case."); - error(); -} - -void omit_error(void) -{ - print_err("Misplaced \\omit"); - help2("I expect to see \\omit only after tab marks or the \\cr of", - "an alignment. Proceed, and I'll ignore this case."); - error(); -} - -@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. -Let's take a look at what happens when they are used correctly. - -An |align_group| code is supposed to remain on the |save_stack| -during an entire alignment, until |fin_align| removes it. - -A devious user might force an |endv| command to occur just about anywhere; -we must defeat such hacks. - -@c -void do_endv(void) -{ - base_ptr = input_ptr; - input_stack[base_ptr] = cur_input; - while ((input_stack[base_ptr].index_field != v_template) && - (input_stack[base_ptr].loc_field == null) && - (input_stack[base_ptr].state_field == token_list)) - decr(base_ptr); - if ((input_stack[base_ptr].index_field != v_template) || - (input_stack[base_ptr].loc_field != null) || - (input_stack[base_ptr].state_field != token_list)) - fatal_error("(interwoven alignment preambles are not allowed)"); - /*.interwoven alignment preambles... */ - if (cur_group == align_group) { - end_graf(align_group); - if (fin_col()) - fin_row(); - } else { - off_save(); - } -} - -@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. - -@c -void cs_error(void) -{ - print_err("Extra \\endcsname"); - help1("I'm ignoring this, since I wasn't doing a \\csname."); - error(); -} - -@ - Assignments to values in |eqtb| can be global or local. Furthermore, a - control sequence can be defined to be `\.{\\long}', `\.{\\protected}', - or `\.{\\outer}', and it might or might not be expanded. The prefixes - `\.{\\global}', `\.{\\long}', `\.{\\protected}', - and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric - codes, making it possible to accumulate the union of all specified prefixes - by adding the corresponding codes. (PASCAL's |set| operations could also - have been used.) - - Every prefix, and every command code that might or might not be prefixed, - calls the action procedure |prefixed_command|. This routine accumulates - a sequence of prefixes until coming to a non-prefix, then it carries out - the command. - -@ If the user says, e.g., `\.{\\global\\global}', the redundancy is -silently accepted. - - -@ The different types of code values have different legal ranges; the -following program is careful to check each case properly. - -@c -#define check_def_code(A) do { \ - if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ - print_err("Invalid code ("); \ - print_int(cur_val); \ - if (p<(A)) \ - tprint("), should be in the range 0.."); \ - else \ - tprint("), should be at most "); \ - print_int(n); \ - help1("I'm going to use 0 instead of that illegal code value."); \ - error(); \ - cur_val=0; \ - } \ -} while (0) - -@ @c -/* -halfword swap_hang_indent(halfword indentation, halfword shape_mode) { - if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) { - return negate(indentation); - } else { - return indentation; - } -} - -halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode) { - if (shape_mode == 2 || shape_mode == 3 || shape_mode == -2 || shape_mode == -3) { - return hsize_par - width - indentation; - } else { - return indentation; - } -} - -*/ - -void prefixed_command(void) -{ - int a; /* accumulated prefix codes so far */ - internal_font_number f; /* identifies a font */ - halfword j; /* index into a \.{\\parshape} specification */ - halfword p, q; /* for temporary short-term use */ - int n; /* ditto */ - boolean e, check_glue; /* should a definition be expanded? or was \.{\\let} not done? */ - mathcodeval mval; /* for handling of \.{\\mathchardef}s */ - a = 0; - while (cur_cmd == prefix_cmd) { - if (!odd(a / cur_chr)) - a = a + cur_chr; - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - if (cur_cmd <= max_non_prefixed_command) { - /* Discard erroneous prefixes and |return| */ - print_err("You can't use a prefix with `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_char('\''); - help2 - ("I'll pretend you didn't say \\long or \\outer or \\global or", - "\\protected."); - back_error(); - return; - } - if (tracing_commands_par > 2) - show_cur_cmd_chr(); - } - /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */ - if (a >= 8) { - j = protected_token; - a = a - 8; - } else { - j = 0; - } - if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) { - print_err("You can't use `\\long' or `\\outer' or `\\protected' with `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - print_char('\''); - help1("I'll pretend you didn't say \\long or \\outer or \\protected here."); - error(); - } - /* Adjust for the setting of \.{\\globaldefs} */ - if (global_defs_par != 0) { - if (global_defs_par < 0) { - if (is_global(a)) - a = a - 4; - } else { - if (!is_global(a)) - a = a + 4; - } - } - switch (cur_cmd) { - case set_font_cmd: - /* Here's an example of the way many of the following routines operate. - (Unfortunately, they aren't all as simple as this.) */ - define(cur_font_loc, data_cmd, cur_chr); - break; - case def_cmd: - /* When a |def| command has been scanned, - |cur_chr| is odd if the definition is supposed to be global, and - |cur_chr>=2| if the definition is supposed to be expanded. */ - - if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0)) - a = a + 4; - e = (cur_chr >= 2); - get_r_token(); - p = cur_cs; - q = scan_toks(true, e); - if (j != 0) { - q = get_avail(); - set_token_info(q, j); - set_token_link(q, token_link(def_ref)); - set_token_link(def_ref, q); - } - define(p, call_cmd + (a % 4), def_ref); - break; - case let_cmd: - n = cur_chr; - if (n == normal) { - get_r_token(); - p = cur_cs; - do { - get_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '=') { - get_token(); - if (cur_cmd == spacer_cmd) - get_token(); - } - } else if (n == normal + 1) { - /* futurelet */ - get_r_token(); - p = cur_cs; - get_token(); - q = cur_tok; - get_token(); - back_input(); - cur_tok = q; - /* look ahead, then back up */ - /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */ - back_input(); - } else { - /* letcharcode */ - scan_int(); - if (cur_val > 0) { - cur_cs = active_to_cs(cur_val, true); - set_token_info(cur_cs, cur_cs + cs_token_flag); - p = cur_cs; - do { - get_token(); - } while (cur_cmd == spacer_cmd); - if (cur_tok == other_token + '=') { - get_token(); - if (cur_cmd == spacer_cmd) - get_token(); - } - } else { - p = null; - tex_error("invalid number for \\letcharcode",NULL); - } - } - if (cur_cmd >= call_cmd) - add_token_ref(cur_chr); - define(p, cur_cmd, cur_chr); - break; - case shorthand_def_cmd: - /* We temporarily define |p| to be |relax|, so that an occurrence of |p| - while scanning the definition will simply stop the scanning instead of - producing an ``undefined control sequence'' error or expanding the - previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. - */ - n = cur_chr; - get_r_token(); - p = cur_cs; - define(p, relax_cmd, too_big_char); - scan_optional_equals(); - switch (n) { - case char_def_code: - scan_char_num(); - define(p, char_given_cmd, cur_val); - break; - case math_char_def_code: - mval = scan_mathchar(tex_mathcode); - if (math_umathcode_meaning_par == 1) { - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - } else { - cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value; - define(p, math_given_cmd, cur_val); - } - break; - case xmath_char_def_code: - mval = scan_mathchar(umath_mathcode); - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - break; - case umath_char_def_code: - mval = scan_mathchar(umathnum_mathcode); - cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; - define(p, xmath_given_cmd, cur_val); - break; - default: - scan_register_num(); - switch (n) { - case count_def_code: - define(p, assign_int_cmd, count_base + cur_val); - break; - case attribute_def_code: - define(p, assign_attr_cmd, attribute_base + cur_val); - break; - case dimen_def_code: - define(p, assign_dimen_cmd, scaled_base + cur_val); - break; - case skip_def_code: - define(p, assign_glue_cmd, skip_base + cur_val); - break; - case mu_skip_def_code: - define(p, assign_mu_glue_cmd, mu_skip_base + cur_val); - break; - case toks_def_code: - define(p, assign_toks_cmd, toks_base + cur_val); - break; - default: - confusion("shorthand_def"); - break; - } - break; - } - break; - case read_to_cs_cmd: - j = cur_chr; - scan_int(); - n = cur_val; - if (!scan_keyword("to")) { - print_err("Missing `to' inserted"); - help2("You should have said `\\read to \\cs'.", - "I'm going to look for the \\cs now."); - error(); - } - get_r_token(); - p = cur_cs; - read_toks(n, p, j); - define(p, call_cmd, cur_val); - break; - case toks_register_cmd: - case assign_toks_cmd: - /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive - their values in the following way. (For safety's sake, we place an - enclosing pair of braces around an \.{\\output} list.) */ - q = cur_cs; - if (cur_cmd == toks_register_cmd) { - scan_register_num(); - p = toks_base + cur_val; - } else { - p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */ - } - scan_optional_equals(); - /* Get the next non-blank non-relax non-call token */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - - if (cur_cmd != left_brace_cmd) { - /* If the right-hand side is a token parameter - or token register, finish the assignment and |goto done| */ - if (cur_cmd == toks_register_cmd) { - scan_register_num(); - cur_cmd = assign_toks_cmd; - cur_chr = toks_base + cur_val; - } - if (cur_cmd == assign_toks_cmd) { - q = equiv(cur_chr); - if (q == null) { - define(p, undefined_cs_cmd, null); - } else { - add_token_ref(q); - define(p, call_cmd, q); - } - goto DONE; - } - } - back_input(); - cur_cs = q; - q = scan_toks(false, false); - if (token_link(def_ref) == null) { /* empty list: revert to the default */ - define(p, undefined_cs_cmd, null); - free_avail(def_ref); - } else { - if (p == output_routine_loc) { /* enclose in curlies */ - p = get_avail(); - set_token_link(q, p); - p = output_routine_loc; - q = token_link(q); - set_token_info(q, right_brace_token + '}'); - q = get_avail(); - set_token_info(q, left_brace_token + '{'); - set_token_link(q, token_link(def_ref)); - set_token_link(def_ref, q); - } - define(p, call_cmd, def_ref); - } - break; - case assign_int_cmd: - /* Similar routines are used to assign values to the numeric parameters. */ - p = cur_chr; - scan_optional_equals(); - scan_int(); - assign_internal_value(a, p, cur_val); - break; - case assign_attr_cmd: - p = cur_chr; - scan_optional_equals(); - scan_int(); - if ((p - attribute_base) > max_used_attr) - max_used_attr = (p - attribute_base); - attr_list_cache = cache_disabled; - word_define(p, cur_val); - break; - case assign_dir_cmd: - /* DIR: Assign direction codes */ - scan_direction(); - switch (cur_chr) { - case int_base + page_direction_code: - eq_word_define(int_base + page_direction_code, cur_val); - break; - case int_base + body_direction_code: - eq_word_define(int_base + body_direction_code, cur_val); - break; - case int_base + par_direction_code: - eq_word_define(int_base + par_direction_code, cur_val); - break; - case int_base + math_direction_code: - eq_word_define(int_base + math_direction_code, cur_val); - break; - case int_base + text_direction_code: - case int_base + line_direction_code: - /* - pre version 0.97 this was a commented section because various tests hint that this - is unnecessary and sometimes even produces weird results, like: - - (\hbox{\textdir TRT ABC\textdir TLT DEF})) - - becomes - - (DEFCBA) - - in the output when we use - - tail_append(new_dir(text_direction_par) - - but when we append the reverse of the current it goes better - - */ - check_glue = (cur_chr == (int_base + line_direction_code)); - if (check_glue) { - cur_chr = int_base + text_direction_code ; - } - if (abs(mode) == hmode) { - if (no_local_dirs_par > 0) { - /* tail is non zero but we test anyway */ - if (check_glue && (tail != null && type(tail) == glue_node)) { - halfword prev = alink(tail); - halfword dirn = new_dir(text_direction_par - dir_swap); - couple_nodes(prev,dirn); - couple_nodes(dirn,tail); - } else { - tail_append(new_dir(text_direction_par - dir_swap)); - } - } else { - /* what is the use of nolocaldirs .. maybe we should get rid of it */ - } - update_text_dir_ptr(cur_val); - tail_append(new_dir(cur_val)); - dir_level(tail) = cur_level; - } else { - update_text_dir_ptr(cur_val); - } - /* original: - - // if ((no_local_dirs_par > 0) && (abs(mode) == hmode)) { - // // tail_append(new_dir(text_direction_par) // kind of wrong - // tail_append(new_dir(text_direction_par - dir_swap)); // better - // } - - update_text_dir_ptr(cur_val); - if (abs(mode) == hmode) { - tail_append(new_dir(cur_val)); - dir_level(tail) = cur_level; - } - */ - eq_word_define(int_base + text_direction_code, cur_val); - eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1); - break; - } - break; - case assign_dimen_cmd: - p = cur_chr; - scan_optional_equals(); - scan_normal_dimen(); - assign_internal_value(a, p, cur_val); - break; - case assign_glue_cmd: - case assign_mu_glue_cmd: - p = cur_chr; - n = cur_cmd; - scan_optional_equals(); - if (n == assign_mu_glue_cmd) - scan_glue(mu_val_level); - else - scan_glue(glue_val_level); - define(p, glue_ref_cmd, cur_val); - break; - case def_char_code_cmd: - case def_del_code_cmd: - /* Let |n| be the largest legal code value, based on |cur_chr| */ - if (cur_chr == cat_code_base) - n = max_char_code; - else if (cur_chr == sf_code_base) - n = 077777; - else - n = biggest_char; - - p = cur_chr; - if (cur_chr == math_code_base) { - if (is_global(a)) - cur_val1 = level_one; - else - cur_val1 = cur_level; - scan_extdef_math_code(cur_val1, tex_mathcode); - } else if (cur_chr == lc_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(lc_code_base); - define_lc_code(p, cur_val); - } else if (cur_chr == uc_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(uc_code_base); - define_uc_code(p, cur_val); - } else if (cur_chr == sf_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(sf_code_base); - define_sf_code(p, cur_val); - } else if (cur_chr == cat_code_base) { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - check_def_code(cat_code_base); - define_cat_code(p, cur_val); - } else if (cur_chr == del_code_base) { - if (is_global(a)) - cur_val1 = level_one; - else - cur_val1 = cur_level; - scan_extdef_del_code(cur_val1, tex_mathcode); - } - break; - case extdef_math_code_cmd: - case extdef_del_code_cmd: - if (is_global(a)) - cur_val1 = level_one; - else - cur_val1 = cur_level; - if (cur_chr == math_code_base) - scan_extdef_math_code(cur_val1, umath_mathcode); - else if (cur_chr == math_code_base + 1) - scan_extdef_math_code(cur_val1, umathnum_mathcode); - else if (cur_chr == del_code_base) - scan_extdef_del_code(cur_val1, umath_mathcode); - else if (cur_chr == del_code_base + 1) - scan_extdef_del_code(cur_val1, umathnum_mathcode); - break; - case def_family_cmd: - p = cur_chr; - scan_math_family_int(); - cur_val1 = cur_val; - scan_optional_equals(); - scan_font_ident(); - define_fam_fnt(cur_val1, p, cur_val); - break; - case set_math_param_cmd: - p = cur_chr; - get_token(); - if (cur_cmd != math_style_cmd) { - print_err("Missing math style, treated as \\displaystyle"); - help1 - ("A style should have been here; I inserted `\\displaystyle'."); - cur_val1 = display_style; - back_error(); - } else { - cur_val1 = cur_chr; - } - scan_optional_equals(); - if (p < math_param_first_mu_glue) { - if (p == math_param_radical_degree_raise) - scan_int(); - else - scan_dimen(false, false, false); - } else { - scan_glue(mu_val_level); - if (cur_val == thin_mu_skip_par) - cur_val = thin_mu_skip_code; - else if (cur_val == med_mu_skip_par) - cur_val = med_mu_skip_code; - else if (cur_val == thick_mu_skip_par) - cur_val = thick_mu_skip_code; - } - define_math_param(p, cur_val1, cur_val); - break; - case register_cmd: - case advance_cmd: - case multiply_cmd: - case divide_cmd: - do_register_command(a); - break; - case set_box_cmd: - /* The processing of boxes is somewhat different, because we may need - to scan and create an entire box before we actually change the value - of the old one. */ - scan_register_num(); - if (is_global(a)) - n = global_box_flag + cur_val; - else - n = box_flag + cur_val; - scan_optional_equals(); - if (set_box_allowed) { - scan_box(n); - } else { - print_err("Improper \\setbox"); - help2("Sorry, \\setbox is not allowed after \\halign in a display,", - "or between \\accent and an accented character."); - error(); - } - break; - case set_aux_cmd: - /* The |space_factor| or |prev_depth| settings are changed when a |set_aux| - command is sensed. Similarly, |prev_graf| is changed in the presence of - |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of - |set_page_int|. These definitions are always global. */ - alter_aux(); - break; - case set_prev_graf_cmd: - alter_prev_graf(); - break; - case set_page_dimen_cmd: - alter_page_so_far(); - break; - case set_page_int_cmd: - alter_integer(); - break; - case set_box_dimen_cmd: - /* When some dimension of a box register is changed, the change isn't exactly - global; but \TeX\ does not look at the \.{\\global} switch. */ - alter_box_dimen(); - break; - case set_tex_shape_cmd: - q = cur_chr; - scan_optional_equals(); - scan_int(); - n = cur_val; - if (n <= 0) { - p = null; - } else { - p = new_node(shape_node, 2 * (n + 1) + 1); - vinfo(p + 1) = n; - for (j = 1; j <= n; j++) { - scan_normal_dimen(); - varmem[p + 2 * j].cint = cur_val; /* indentation */ - scan_normal_dimen(); - varmem[p + 2 * j + 1].cint = cur_val; /* width */ - } - } - define(q, shape_ref_cmd, p); - break; - case set_etex_shape_cmd: - q = cur_chr; - scan_optional_equals(); - scan_int(); - n = cur_val; - if (n <= 0) { - p = null; - } else { - n = (cur_val / 2) + 1; - p = new_node(shape_node, 2 * n + 1 + 1); - vinfo(p + 1) = n; - n = cur_val; - varmem[p + 2].cint = n; /* number of penalties */ - for (j = p + 3; j <= p + n + 2; j++) { - scan_int(); - varmem[j].cint = cur_val; /* penalty values */ - } - if (!odd(n)) - varmem[p + n + 3].cint = 0; /* unused */ - } - define(q, shape_ref_cmd, p); - break; - case hyph_data_cmd: - /* All of \TeX's parameters are kept in |eqtb| except the font information, - the interaction mode, and the hyphenation tables; these are strictly global. - */ - switch (cur_chr) { - case 0: - new_hyph_exceptions(); - break; - case 1: - new_patterns(); - break; - case 2: - new_pre_hyphen_char(); - break; - case 3: - new_post_hyphen_char(); - break; - case 4: - new_pre_exhyphen_char(); - break; - case 5: - new_post_exhyphen_char(); - break; - case 6: - new_hyphenation_min(); - break; - case 7: - new_hj_code(); - break; - } - break; - case assign_font_dimen_cmd: - set_font_dimen(); - break; - case assign_font_int_cmd: - n = cur_chr; - scan_font_ident(); - f = cur_val; - if (n == no_lig_code) { - set_no_ligatures(f); - } else if (n < lp_code_base) { - scan_optional_equals(); - scan_int(); - if (n == 0) - set_hyphen_char(f, cur_val); - else - set_skew_char(f, cur_val); - } else { - scan_char_num(); - p = cur_val; - scan_optional_equals(); - scan_int(); - switch (n) { - case lp_code_base: - set_lp_code(f, p, cur_val); - break; - case rp_code_base: - set_rp_code(f, p, cur_val); - break; - case ef_code_base: - set_ef_code(f, p, cur_val); - break; - case tag_code: - set_tag_code(f, p, cur_val); - break; - } - } - break; - case def_font_cmd: - /* Here is where the information for a new font gets loaded. */ - tex_def_font((small_number) a); - break; - case letterspace_font_cmd: - new_letterspaced_font((small_number) a); - break; - case copy_font_cmd: - make_font_copy((small_number) a); - break; - case set_font_id_cmd: - scan_int(); - if (is_valid_font(cur_val)) - zset_cur_font(cur_val); - break ; - case set_interaction_cmd: - new_interaction(); - break; - default: - confusion("prefix"); - break; - } /* end of Assignments cases */ - DONE: - /* Insert a token saved by \.{\\afterassignment}, if any */ - if (after_token != 0) { - cur_tok = after_token; - back_input(); - after_token = 0; - } -} - -@ @c -void fixup_directions(void) -{ - int temp_no_whatsits = no_local_whatsits_par; - int temp_no_dirs = no_local_dirs_par; - int temporary_dir = text_direction_par; - if (dir_level(text_dir_ptr) == cur_level) { - /* DIR: Remove from |text_dir_ptr| */ - halfword text_dir_tmp = vlink(text_dir_ptr); - flush_node(text_dir_ptr); - text_dir_ptr = text_dir_tmp; - } - unsave(); - if (abs(mode) == hmode) { - if (temp_no_dirs != 0) { - /* DIR: Add local dir node */ - tail_append(new_dir(text_direction_par)); - dir_dir(tail) = temporary_dir - dir_swap; - } - if (temp_no_whatsits != 0) { - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(hmode_par_par_code)); - } - } -} - -@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or -something similar, the |get_r_token| routine will substitute a special -control sequence for a token that is not redefinable. - -@c -void get_r_token(void) -{ - RESTART: - do { - get_token(); - } while (cur_tok == space_token); - if ((cur_cs == 0) || (cur_cs > eqtb_top) || - ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) { - print_err("Missing control sequence inserted"); - help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", - "I've inserted an inaccessible control sequence so that your", - "definition will be completed without mixing me up too badly.", - "You can recover graciously from this error, if you're", - "careful; see exercise 27.2 in The TeXbook."); - if (cur_cs == 0) - back_input(); - cur_tok = cs_token_flag + frozen_protection; - ins_error(); - goto RESTART; - } -} - -@ @c -void assign_internal_value(int a, halfword p, int val) -{ - halfword n; - if ((p >= int_base) && (p < attribute_base)) { - switch ((p - int_base)) { - case cat_code_table_code: - if (valid_catcode_table(val)) { - if (val != cat_code_table_par) - word_define(p, val); - } else { - print_err("Invalid \\catcode table"); - help2 - ("You can only switch to a \\catcode table that is initialized", - "using \\savecatcodetable or \\initcatcodetable, or to table 0"); - error(); - } - break; - case output_box_code: - if ((val > 65535) | (val < 0)) { - print_err("Invalid \\outputbox"); - help1 - ("The value for \\outputbox has to be between 0 and 65535."); - error(); - } else { - word_define(p, val); - } - break; - case new_line_char_code: - if (val > 127) { - print_err("Invalid \\newlinechar"); - help2 - ("The value for \\newlinechar has to be no higher than 127.", - "Your invalid assignment will be ignored."); - error(); - } else { - word_define(p, val); - } - break; - case end_line_char_code: - if (val > 127) { - print_err("Invalid \\endlinechar"); - help2 - ("The value for \\endlinechar has to be no higher than 127.", - "Your invalid assignment will be ignored."); - error(); - } else { - word_define(p, val); - } - break; - case language_code: - if (val < 0) { - word_define(int_base + cur_lang_code, -1); - word_define(p, -1); - } else if (val > 16383) { - print_err("Invalid \\language"); - help2 - ("The absolute value for \\language has to be no higher than 16383.", - "Your invalid assignment will be ignored."); - error(); - } else { - word_define(int_base + cur_lang_code, val); - word_define(p, val); - } - break; - default: - word_define(p, val); - break; - } - /* If we are defining subparagraph penalty levels while we are - in hmode, then we put out a whatsit immediately, otherwise - we leave it alone. This mechanism might not be sufficiently - powerful, and some other algorithm, searching down the stack, - might be necessary. Good first step. */ - if ((abs(mode) == hmode) && - ((p == (int_base + local_inter_line_penalty_code)) || - (p == (int_base + local_broken_penalty_code)))) { - /* LOCAL: Add local paragraph node */ - tail_append(make_local_par_node(penalty_par_code)); - eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); - } - } else if ((p >= dimen_base) && (p <= eqtb_size)) { - if (p == (dimen_base + page_left_offset_code)) { - n = val - one_true_inch; - word_define(dimen_base + h_offset_code, n); - } else if (p == (dimen_base + h_offset_code)) { - n = val + one_true_inch; - word_define(dimen_base + page_left_offset_code, n); - } else if (p == (dimen_base + page_top_offset_code)) { - n = val - one_true_inch; - word_define(dimen_base + v_offset_code, n); - } else if (p == (dimen_base + v_offset_code)) { - n = val + one_true_inch; - word_define(dimen_base + page_top_offset_code, n); - } - word_define(p, val); - } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */ - define(p, call_cmd, val); - } else { - confusion("assign internal value"); - } -} - -@ We use the fact that |register= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) { - l = cur_chr; - p = cur_cmd - assign_int_cmd; - goto FOUND; - } - if (cur_cmd != register_cmd) { - print_err("You can't use `"); - print_cmd_chr((quarterword) cur_cmd, cur_chr); - tprint("' after "); - print_cmd_chr((quarterword) q, 0); - help1("I'm forgetting what you said and not changing anything."); - error(); - return; - } - } - p = cur_chr; - scan_register_num(); - if (p == int_val_level) - l = cur_val + count_base; - else if (p == attr_val_level) - l = cur_val + attribute_base; - else if (p == dimen_val_level) - l = cur_val + scaled_base; - else if (p == glue_val_level) - l = cur_val + skip_base; - else if (p == mu_val_level) - l = cur_val + mu_skip_base; - FOUND: - if (q == register_cmd) { - scan_optional_equals(); - } else if (scan_keyword("by")) { - /* optional `\.{by}' */ - } - arith_error = false; - if (q < multiply_cmd) { - /* Compute result of |register| or |advance|, put it in |cur_val| */ - if (p < glue_val_level) { - if ((p == int_val_level) || (p == attr_val_level)) - scan_int(); - else - scan_normal_dimen(); - if (q == advance_cmd) - cur_val = cur_val + eqtb[l].cint; - } else { - /* we can probably save a copy */ - scan_glue(p); - if (q == advance_cmd) { - /* Compute the sum of two glue specs */ - halfword r = equiv(l); - q = new_spec(cur_val); - flush_node(cur_val); - width(q) = width(q) + width(r); - if (stretch(q) == 0) { - stretch_order(q) = normal; - } - if (stretch_order(q) == stretch_order(r)) { - stretch(q) = stretch(q) + stretch(r); - } else if ((stretch_order(q) < stretch_order(r)) && (stretch(r) != 0)) { - stretch(q) = stretch(r); - stretch_order(q) = stretch_order(r); - } - if (shrink(q) == 0) { - shrink_order(q) = normal; - } - if (shrink_order(q) == shrink_order(r)) { - shrink(q) = shrink(q) + shrink(r); - } else if ((shrink_order(q) < shrink_order(r)) && (shrink(r) != 0)) { - shrink(q) = shrink(r); - shrink_order(q) = shrink_order(r); - } - cur_val = q; - } - } - } else { - /* Compute result of |multiply| or |divide|, put it in |cur_val| */ - scan_int(); - if (p < glue_val_level) { - if (q == multiply_cmd) { - if ((p == int_val_level) || (p == attr_val_level)) { - cur_val = mult_integers(eqtb[l].cint, cur_val); - } else { - cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0); - } - } else { - cur_val = x_over_n(eqtb[l].cint, cur_val); - } - } else { - halfword s = equiv(l); - halfword r = new_spec(s); - if (q == multiply_cmd) { - width(r) = nx_plus_y(width(s), cur_val, 0); - stretch(r) = nx_plus_y(stretch(s), cur_val, 0); - shrink(r) = nx_plus_y(shrink(s), cur_val, 0); - } else { - width(r) = x_over_n(width(s), cur_val); - stretch(r) = x_over_n(stretch(s), cur_val); - shrink(r) = x_over_n(shrink(s), cur_val); - } - cur_val = r; - } - } - if (arith_error) { - print_err("Arithmetic overflow"); - help2("I can't carry out that multiplication or division,", - "since the result is out of range."); - if (p >= glue_val_level) - flush_node(cur_val); - error(); - return; - } - if (p < glue_val_level) { - if (p == attr_val_level) { - if ((l - attribute_base) > max_used_attr) - max_used_attr = (l - attribute_base); - attr_list_cache = cache_disabled; - } - if ((p == int_val_level) || (p == dimen_val_level)) - assign_internal_value(a, l, cur_val); - else - word_define(l, cur_val); - } else { - define(l, glue_ref_cmd, cur_val); - } -} - -@ @c -void alter_aux(void) -{ - halfword c; /* |hmode| or |vmode| */ - if (cur_chr != abs(mode)) { - report_illegal_case(); - } else { - c = cur_chr; - scan_optional_equals(); - if (c == vmode) { - scan_normal_dimen(); - prev_depth_par = cur_val; - } else { - scan_int(); - if ((cur_val <= 0) || (cur_val > 32767)) { - print_err("Bad space factor"); - help1("I allow only values in the range 1..32767 here."); - int_error(cur_val); - } else { - space_factor_par = cur_val; - } - } - } -} - -@ @c -void alter_prev_graf(void) -{ - int p; /* index into |nest| */ - p = nest_ptr; - while (abs(nest[p].mode_field) != vmode) - decr(p); - scan_optional_equals(); - scan_int(); - if (cur_val < 0) { - print_err("Bad \\prevgraf"); - help1("I allow only nonnegative values here."); - int_error(cur_val); - } else { - nest[p].pg_field = cur_val; - } -} - -@ @c -void alter_page_so_far(void) -{ - int c; /* index into |page_so_far| */ - c = cur_chr; - scan_optional_equals(); - scan_normal_dimen(); - page_so_far[c] = cur_val; -} - -@ @c -void alter_integer(void) -{ - int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */ - c = cur_chr; - scan_optional_equals(); - scan_int(); - if (c == 0) { - dead_cycles = cur_val; - } else if (c == 2) { - if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) { - print_err("Bad interaction mode"); - help2("Modes are 0=batch, 1=nonstop, 2=scroll, and", - "3=errorstop. Proceed, and I'll ignore this case."); - int_error(cur_val); - } else { - cur_chr = cur_val; - new_interaction(); - } - } else { - insert_penalties = cur_val; - } -} - -@ @c -void alter_box_dimen(void) -{ - int c; /* |width_offset| or |height_offset| or |depth_offset| */ - int b; /* box number */ - c = cur_chr; - scan_register_num(); - b = cur_val; - scan_optional_equals(); - scan_normal_dimen(); - if (box(b) != null) - varmem[box(b) + c].cint = cur_val; -} - -@ @c -void new_interaction(void) -{ - print_ln(); - interaction = cur_chr; - if (interaction == batch_mode) - kpse_make_tex_discard_errors = 1; - else - kpse_make_tex_discard_errors = 0; - fixup_selector(log_opened_global); -} - -@ The \.{\\afterassignment} command puts a token into the global -variable |after_token|. This global variable is examined just after -every assignment has been performed. - -@c -halfword after_token; /* zero, or a saved token */ - -@ Here is a procedure that might be called `Get the next non-blank non-relax -non-call non-assignment token'. - -@c -void do_assignments(void) -{ - while (true) { - /* Get the next non-blank non-relax... */ - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - if (cur_cmd <= max_non_prefixed_command) - return; - set_box_allowed = false; - prefixed_command(); - set_box_allowed = true; - } -} - -@ @c -void open_or_close_in(void) -{ - int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */ - int n; /* stream number */ - char *fn; - c = cur_chr; - scan_four_bit_int(); - n = cur_val; - if (read_open[n] != closed) { - lua_a_close_in(read_file[n], (n + 1)); - read_open[n] = closed; - } - if (c != 0) { - scan_optional_equals(); - do { - get_x_token(); - } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); - back_input(); - if (cur_cmd != left_brace_cmd) { - scan_file_name(); /* set |cur_name| to desired file name */ - if (cur_ext == get_nullstr()) - cur_ext = maketexstring(".tex"); - } else { - scan_file_name_toks(); - } - fn = pack_file_name(cur_name, cur_area, cur_ext); - if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) { - read_open[n] = just_open; - } - } -} - -@ @c -boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ - -void issue_message(void) -{ - int old_setting; /* holds |selector| setting */ - int c; /* identifies \.{\\message} and \.{\\errmessage} */ - str_number s; /* the message */ - c = cur_chr; - (void) scan_toks(false, true); - old_setting = selector; - selector = new_string; - token_show(def_ref); - selector = old_setting; - flush_list(def_ref); - str_room(1); - s = make_string(); - if (c == 0) { - /* Print string |s| on the terminal */ - if (term_offset + (int) str_length(s) > max_print_line - 2) - print_ln(); - else if ((term_offset > 0) || (file_offset > 0)) - print_char(' '); - print(s); - update_terminal(); - - } else { - /* Print string |s| as an error message */ - /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined - \.{\\errhelp}, we don't want to give a long help message each time. So we - give a verbose explanation only once. */ - print_err(""); - print(s); - if (err_help_par != null) { - use_err_help = true; - } else if (long_help_seen) { - help1("(That was another \\errmessage.)"); - } else { - if (interaction < error_stop_mode) - long_help_seen = true; - help4("This error message was generated by an \\errmessage", - "command, so I can't give any explicit help.", - "Pretend that you're Hercule Poirot: Examine all clues,", - "and deduce the truth by order and method."); - } - error(); - use_err_help = false; - - } - flush_str(s); -} - -@ The |error| routine calls on |give_err_help| if help is requested from -the |err_help| parameter. - -@c -void give_err_help(void) -{ - token_show(err_help_par); -} - -@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by -building a token list and then changing the cases of the letters in it. - -@c -void shift_case(void) -{ - halfword b; /* |lc_code_base| or |uc_code_base| */ - halfword p; /* runs through the token list */ - halfword t; /* token */ - halfword c; /* character code */ - halfword i; /* inbetween */ - b = cur_chr; - p = scan_toks(false, false); - p = token_link(def_ref); - while (p != null) { - /* Change the case of the token in |p|, if a change is appropriate */ - /* - When the case of a |chr_code| changes, we don't change the |cmd|. - We also change active characters. - */ - t = token_info(p); - if (t < cs_token_flag) { - c = t % STRING_OFFSET; - if (b == uc_code_base) - i = get_uc_code(c); - else - i = get_lc_code(c); - if (i != 0) - set_token_info(p, t - c + i); - } else if (is_active_cs(cs_text(t - cs_token_flag))) { - c = active_cs_value(cs_text(t - cs_token_flag)); - if (b == uc_code_base) - i = get_uc_code(c); - else - i = get_lc_code(c); - if (i != 0) - set_token_info(p, active_to_cs(i, true) + cs_token_flag); - } - p = token_link(p); - } - back_list(token_link(def_ref)); - free_avail(def_ref); /* omit reference count */ -} - -@ We come finally to the last pieces missing from |main_control|, namely the -`\.{\\show}' commands that are useful when debugging. - -@c -void show_whatever(void) -{ - halfword p; /* tail of a token list to show */ - int t; /* type of conditional being shown */ - int m; /* upper bound on |fi_or_else| codes */ - int l; /* line where that conditional began */ - int n; /* level of \.{\\if...\\fi} nesting */ - switch (cur_chr) { - case show_lists: - begin_diagnostic(); - show_activities(); - break; - case show_box_code: - /* Show the current contents of a box */ - scan_register_num(); - begin_diagnostic(); - tprint_nl("> \\box"); - print_int(cur_val); - print_char('='); - if (box(cur_val) == null) - tprint("void"); - else - show_box(box(cur_val)); - break; - case show_code: - /* Show the current meaning of a token, then |goto common_ending| */ - get_token(); - if (interaction == error_stop_mode) - wake_up_terminal(); - tprint_nl("> "); - if (cur_cs != 0) { - sprint_cs(cur_cs); - print_char('='); - } - print_meaning(); - goto COMMON_ENDING; - break; - /* Cases for |show_whatever| */ - case show_groups: - begin_diagnostic(); - show_save_groups(); - break; - case show_ifs: - begin_diagnostic(); - tprint_nl(""); - print_ln(); - if (cond_ptr == null) { - tprint_nl("### "); - tprint("no active conditionals"); - } else { - p = cond_ptr; - n = 0; - do { - incr(n); - p = vlink(p); - } while (p != null); - p = cond_ptr; - t = cur_if; - l = if_line; - m = if_limit; - do { - tprint_nl("### level "); - print_int(n); - tprint(": "); - print_cmd_chr(if_test_cmd, t); - if (m == fi_code) - tprint_esc("else"); - print_if_line(l); - decr(n); - t = if_limit_subtype(p); - l = if_line_field(p); - m = if_limit_type(p); - p = vlink(p); - } while (p != null); - } - break; - default: - /* Show the current value of some parameter or register, - then |goto common_ending| */ - p = the_toks(); - if (interaction == error_stop_mode) - wake_up_terminal(); - tprint_nl("> "); - token_show(temp_token_head); - flush_list(token_link(temp_token_head)); - goto COMMON_ENDING; - break; - } - /* Complete a potentially long \.{\\show} command */ - end_diagnostic(true); - print_err("OK"); - if (selector == term_and_log) { - if (tracing_online_par <= 0) { - selector = term_only; - tprint(" (see the transcript file)"); - selector = term_and_log; - } - } - COMMON_ENDING: - if (interaction < error_stop_mode) { - help0(); - decr(error_count); - } else if (tracing_online_par > 0) { - help3("This isn't an error message; I'm just \\showing something.", - "Type `I\\show...' to show more (e.g., \\show\\cs,", - "\\showthe\\count10, \\showbox255, \\showlists)."); - } else { - help5("This isn't an error message; I'm just \\showing something.", - "Type `I\\show...' to show more (e.g., \\show\\cs,", - "\\showthe\\count10, \\showbox255, \\showlists).", - "And type `I\\tracingonline=1\\show...' to show boxes and", - "lists on your terminal as well as in the transcript file."); - } - error(); -} - -@ @c -void initialize(void) -{ /* this procedure gets things started properly */ - int k; /* index into |mem|, |eqtb|, etc. */ - /* Initialize whatever \TeX\ might access */ - /* Set initial values of key variables */ - initialize_errors(); - initialize_arithmetic(); - max_used_attr = -1; - attr_list_cache = cache_disabled; - initialize_nesting(); - - /* Start a new current page */ - page_contents = empty; - page_tail = page_head; -#if 0 - vlink(page_head) = null; -#endif - last_glue = max_halfword; - last_penalty = 0; - last_kern = 0; - last_node_type = -1; - page_depth = 0; - page_max_depth = 0; - - initialize_equivalents(); - no_new_control_sequence = true; /* new identifiers are usually forbidden */ - init_primitives(); - - mag_set = 0; - initialize_marks(); - initialize_read(); - - static_pdf = init_pdf_struct(static_pdf); /* should be init_backend() */ - - format_ident = 0; - format_name = get_nullstr(); - initialize_directions(); - initialize_write_files(); - seconds_and_micros(epochseconds, microseconds); - initialize_start_time(static_pdf); - - edit_name_start = 0; - stop_at_space = true; - - if (ini_version) { - /* Initialize table entries (done by \.{INITEX} only) */ - - init_node_mem(500); - initialize_tokens(); - /* Initialize the special list heads and constant nodes */ - initialize_alignments(); - initialize_buildpage(); - - initialize_active(); - - set_eq_type(undefined_control_sequence, undefined_cs_cmd); - set_equiv(undefined_control_sequence, null); - set_eq_level(undefined_control_sequence, level_zero); - for (k = null_cs; k <= (eqtb_top - 1); k++) - eqtb[k] = eqtb[undefined_control_sequence]; - set_equiv(glue_base, zero_glue); - set_eq_level(glue_base, level_one); - set_eq_type(glue_base, glue_ref_cmd); - for (k = glue_base + 1; k <= local_base - 1; k++) { - eqtb[k] = eqtb[glue_base]; - } - par_shape_par_ptr = null; - set_eq_type(par_shape_loc, shape_ref_cmd); - set_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 + biggest_reg; k++) - eqtb[k] = eqtb[undefined_control_sequence]; - box(0) = null; - set_eq_type(box_base, box_ref_cmd); - set_eq_level(box_base, level_one); - for (k = box_base + 1; k <= (box_base + biggest_reg); k++) - eqtb[k] = eqtb[box_base]; - cur_font_par = null_font; - set_eq_type(cur_font_loc, data_cmd); - set_eq_level(cur_font_loc, level_one); - set_equiv(cat_code_base, 0); - set_eq_type(cat_code_base, data_cmd); - set_eq_level(cat_code_base, level_one); - eqtb[internal_math_param_base] = eqtb[cat_code_base]; - eqtb[lc_code_base] = eqtb[cat_code_base]; - eqtb[uc_code_base] = eqtb[cat_code_base]; - eqtb[sf_code_base] = eqtb[cat_code_base]; - eqtb[math_code_base] = eqtb[cat_code_base]; - cat_code_table_par = 0; - initialize_math_codes(); - initialize_text_codes(); - initex_cat_codes(0); - for (k = '0'; k <= '9'; k++) - set_math_code(k, math_use_current_family_code, 0, k, level_one); - for (k = 'A'; k <= 'Z'; k++) { - set_math_code(k, math_use_current_family_code, 1, k, level_one); - set_math_code((k + 32), math_use_current_family_code, 1, (k + 32), level_one); - set_lc_code(k, k + 32, level_one); - set_lc_code(k + 32, k + 32, level_one); - set_uc_code(k, k, level_one); - set_uc_code(k + 32, k, level_one); - set_sf_code(k, 999, level_one); - } - for (k = int_base; k <= attribute_base - 1; k++) - eqtb[k].cint = 0; - for (k = attribute_base; k <= del_code_base - 1; k++) - eqtb[k].cint = UNUSED_ATTRIBUTE; - mag_par = 1000; - tolerance_par = 10000; - hang_after_par = 1; - max_dead_cycles_par = 25; - math_pre_display_gap_factor_par = 2000; - escape_char_par = '\\'; - end_line_char_par = carriage_return; - set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */ - ex_hyphen_char_par = '-'; - output_box_par = 255; - for (k = dimen_base; k <= eqtb_size; k++) - eqtb[k].cint = 0; - page_left_offset_par = one_inch; - page_top_offset_par = one_inch; - page_right_offset_par = one_inch; - page_bottom_offset_par = one_inch; - ini_init_primitives(); - hash_used = frozen_control_sequence; /* nothing is used */ - hash_high = 0; - cs_count = 0; - set_eq_type(frozen_dont_expand, dont_expand_cmd); - cs_text(frozen_dont_expand) = maketexstring("notexpanded:"); - set_eq_type(frozen_primitive, ignore_spaces_cmd); - set_equiv(frozen_primitive, 1); - set_eq_level(frozen_primitive, level_one); - cs_text(frozen_primitive) = maketexstring("primitive"); - create_null_font(); - font_bytes = 0; - px_dimen_par = one_bp; - math_eqno_gap_step_par = 1000 ; - cs_text(frozen_protection) = maketexstring("inaccessible"); - format_ident = maketexstring(" (INITEX)"); - cs_text(end_write) = maketexstring("endwrite"); - set_eq_level(end_write, level_one); - set_eq_type(end_write, outer_call_cmd); - set_equiv(end_write, null); - - } - synctexoffset = int_base + synctex_code; - -} +% maincontrol.w +% +% Copyright 2009-2010 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +@ @c +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +@ We come now to the |main_control| routine, which contains the master +switch that causes all the various pieces of \TeX\ to do their things, +in the right order. + +In a sense, this is the grand climax of the program: It applies all the +tools that we have worked so hard to construct. In another sense, this is +the messiest part of the program: It necessarily refers to other pieces +of code all over the place, so that a person can't fully understand what is +going on without paging back and forth to be reminded of conventions that +are defined elsewhere. We are now at the hub of the web, the central nervous +system that touches most of the other parts and ties them together. +@^brain@> + +The structure of |main_control| itself is quite simple. There's a label +called |big_switch|, at which point the next token of input is fetched +using |get_x_token|. Then the program branches at high speed into one of +about 100 possible directions, based on the value of the current +mode and the newly fetched command code; the sum |abs(mode)+cur_cmd| +indicates what to do next. For example, the case `|vmode+letter|' arises +when a letter occurs in vertical mode (or internal vertical mode); this +case leads to instructions that initialize a new paragraph and enter +horizontal mode. + +The big |case| statement that contains this multiway switch has been labeled +|reswitch|, so that the program can |goto reswitch| when the next token +has already been fetched. Most of the cases are quite short; they call +an ``action procedure'' that does the work for that case, and then they +either |goto reswitch| or they ``fall through'' to the end of the |case| +statement, which returns control back to |big_switch|. Thus, |main_control| +is not an extremely large procedure, in spite of the multiplicity of things +it must do; it is small enough to be handled by PASCAL compilers that put +severe restrictions on procedure size. +@!@^action procedure@> + +One case is singled out for special treatment, because it accounts for most +of \TeX's activities in typical applications. The process of reading simple +text and converting it into |char_node| records, while looking for ligatures +and kerns, is part of \TeX's ``inner loop''; the whole program runs +efficiently when its inner loop is fast, so this part has been written +with particular care. + +We leave the |space_factor| unchanged if |sf_code(cur_chr)=0|; otherwise we +set it equal to |sf_code(cur_chr)|, except that it should never change +from a value less than 1000 to a value exceeding 1000. The most common +case is |sf_code(cur_chr)=1000|, so we want that case to be fast. + +@c +void adjust_space_factor(void) +{ + halfword s = get_sf_code(cur_chr); + if (s == 1000) { + space_factor_par = 1000; + } else if (s < 1000) { + if (s > 0) + space_factor_par = s; + } else if (space_factor_par < 1000) { + space_factor_par = 1000; + } else { + space_factor_par = s; + } +} + +@ From Knuth: ``Having |font_glue| allocated for each text font saves +both time and memory.'' That may be true, but it also punches through +the API wall for fonts, so I removed that -- Taco. But a bit of caching +is very welcome, which is why I need to have the next two globals: + +@ To handle the execution state of |main_control|'s eternal loop, +an extra global variable is used, along with a macro to define +its values. + +@c +#define goto_next 0 +#define goto_skip_token 1 +#define goto_return 2 + +static int main_control_state; + +@* Main control helpers. + +Here are all the functions that are called from |main_control| that +are not already defined elsewhere. For the moment, this list simply +in the order that the appear in |init_main_control|, below. + +@ +@c +static void run_char_num (void) { + scan_char_num(); + cur_chr = cur_val; + adjust_space_factor(); + tail_append(new_char(cur_font_par, cur_chr)); +} + +static void run_char (void) { + adjust_space_factor(); + tail_append(new_char(cur_font_par, cur_chr)); +} + +@ +The occurrence of blank spaces is almost part of \TeX's inner loop, +since we usually encounter about one space for every five non-blank characters. +Therefore |main_control| gives second-highest priority to ordinary spaces. + +When a glue parameter like \.{\\spaceskip} is set to `\.{0pt}', we will +see to it later that the corresponding glue specification is precisely +|zero_glue|, not merely a pointer to some specification that happens +to be full of zeroes. Therefore it is simple to test whether a glue parameter +is zero or~not. + +@c +static void run_app_space (void) { + halfword p; /* was a global temp_ptr */ + int method = disable_space_par ; + if (method == 1) { + /* don't inject anything, not even zero skip */ + } else if (method == 2) { + p = new_glue(zero_glue); + couple_nodes(tail,p); + tail = p; + } else if ((abs(mode) + cur_cmd == hmode + spacer_cmd) && (!(space_factor_par == 1000))) { + app_space(); + } else { + /* Append a normal inter-word space to the current list */ + if (glue_is_zero(space_skip_par)) { + /* Find the glue specification for text spaces in the current font */ + p = new_glue(zero_glue); + width(p) = space(cur_font_par); + stretch(p) = space_stretch(cur_font_par); + shrink(p) = space_shrink(cur_font_par); + + } else { + p = new_param_glue(space_skip_code); + } + /* so from now we have a subtype with spaces: */ + subtype(p) = space_skip_code + 1 ; + couple_nodes(tail,p); + tail = p; + } +} + +@ Append a |boundary_node| +@c +static void run_boundary (void) { + halfword n ; + n = new_node(boundary_node,cur_chr); + if ((cur_chr == 1) || (cur_chr == 2) ) { + /* user boundary or protrusion boundary */ + scan_int(); + boundary_value(n) = cur_val; + } + couple_nodes(tail, n); + tail = n; +} + +@ @c +static void run_char_ghost (void) { + int t; + t = cur_chr; + get_x_token(); + if ((cur_cmd == letter_cmd) || (cur_cmd == other_char_cmd) + || (cur_cmd == char_given_cmd) || (cur_cmd == char_num_cmd)) { + halfword p = new_glyph(get_cur_font(), cur_chr); + if (t == 0) { + set_is_leftghost(p); + } else { + set_is_rightghost(p); + } + tail_append(p); + } +} + +@ @c +static void run_relax (void) { + return; +} + +@ |ignore_spaces| is a special case: after it has acted, |get_x_token| has already +fetched the next token from the input, so that operation in |main_control| +should be skipped. + +@c +static void run_ignore_spaces (void) { + if (cur_chr == 0) { + /* Get the next non-blank non-call... */ + do { + get_x_token(); + } while (cur_cmd == spacer_cmd); + main_control_state = goto_skip_token; + } else { + int t = scanner_status; + scanner_status = normal; + get_next(); + scanner_status = t; + cur_cs = prim_lookup(cs_text(cur_cs)); + if (cur_cs != undefined_primitive) { + cur_cmd = get_prim_eq_type(cur_cs); + cur_chr = get_prim_equiv(cur_cs); + cur_tok = (cur_cmd * STRING_OFFSET) + cur_chr; + main_control_state = goto_skip_token; + } + } +} + +@ |stop| is the second special case. We want |main_control| to return to its caller +if there is nothing left to do. + +@c +static void run_stop (void) { + if (its_all_over()) + main_control_state= goto_return; /* this is the only way out */ +} + +@ @c +static void run_non_math_math (void) { + back_input(); + new_graf(true); +} + +@ @c +static void run_math_char_num (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else + mval = scan_mathchar(umathnum_mathcode); + math_char_in_text(mval); +} + +@ @c +static void run_math_given (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + mval = mathchar_from_integer(cur_chr, tex_mathcode); + math_char_in_text(mval); +} + +static void run_xmath_given (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + mval = mathchar_from_integer(cur_chr, umath_mathcode); + math_char_in_text(mval); +} + +@ The most important parts of |main_control| are concerned with \TeX's +chief mission of box-making. We need to control the activities that put +entries on vlists and hlists, as well as the activities that convert +those lists into boxes. All of the necessary machinery has already been +developed; it remains for us to ``push the buttons'' at the right times. + +As an introduction to these routines, let's consider one of the simplest +cases: What happens when `\.{\\hrule}' occurs in vertical mode, or +`\.{\\vrule}' in horizontal mode or math mode? The code in |main_control| +is short, since the |scan_rule_spec| routine already does most of what is +required; thus, there is no need for a special action procedure. + +Note that baselineskip calculations are disabled after a rule in vertical +mode, by setting |prev_depth:=ignore_depth|. + +@c +static void run_rule (void) { + tail_append(scan_rule_spec()); + if (abs(mode) == vmode) + prev_depth_par = ignore_depth; + else if (abs(mode) == hmode) + space_factor_par = 1000; +} + +@ +Many of the actions related to box-making are triggered by the appearance +of braces in the input. For example, when the user says `\.{\\hbox} +\.{to} \.{100pt\{$\langle\,\hbox{hlist}\,\rangle$\}}' in vertical mode, +the information about the box size (100pt, |exactly|) is put onto |save_stack| +with a level boundary word just above it, and |cur_group:=adjusted_hbox_group|; +\TeX\ enters restricted horizontal mode to process the hlist. The right +brace eventually causes |save_stack| to be restored to its former state, +at which time the information about the box size (100pt, |exactly|) is +available once again; a box is packaged and we leave restricted horizontal +mode, appending the new box to the current list of the enclosing mode +(in this case to the current list of vertical mode), followed by any +vertical adjustments that were removed from the box by |hpack|. + +The next few sections of the program are therefore concerned with the +treatment of left and right curly braces. + +If a left brace occurs in the middle of a page or paragraph, it simply +introduces a new level of grouping, and the matching right brace will not have +such a drastic effect. Such grouping affects neither the mode nor the +current list. + +@c +static void run_left_brace (void) { + new_save_level(simple_group); + eq_word_define(int_base + no_local_whatsits_code, 0); + eq_word_define(int_base + no_local_dirs_code, 0); +} + +static void run_begin_group (void) { + new_save_level(semi_simple_group); + eq_word_define(int_base + no_local_whatsits_code, 0); + eq_word_define(int_base + no_local_dirs_code, 0); +} + +static void run_end_group (void) { + if (cur_group == semi_simple_group) { + fixup_directions(); + } else { + off_save(); + } +} + +@ Constructions that require a box are started by calling |scan_box| with +a specified context code. The |scan_box| routine verifies +that a |make_box| command comes next and then it calls |begin_box|. + +@c +static void run_move (void) { + int t = cur_chr; + scan_normal_dimen(); + if (t == 0) + scan_box(cur_val); + else + scan_box(-cur_val); +} + +@ @c +static void run_leader_ship (void) { + scan_box(leader_flag - a_leaders + cur_chr); +} + +@ @c +static void run_make_box (void) { + begin_box(0); +} + +@ @c +static void run_box_dir (void) { + scan_register_num(); + cur_box = box(cur_val); + scan_optional_equals(); + scan_direction(); + if (cur_box != null) + box_dir(cur_box) = cur_val; +} + +@ There is a really small patch to add a new primitive called +\.{\\quitvmode}. In vertical modes, it is identical to \.{\\indent}, +but in horizontal and math modes it is really a no-op (as opposed to +\.{\\indent}, which executes the |indent_in_hmode| procedure). + +A paragraph begins when horizontal-mode material occurs in vertical mode, +or when the paragraph is explicitly started by `\.{\\quitvmode}', +`\.{\\indent}' or `\.{\\noindent}'. + +@c +static void run_start_par_vmode (void) { + new_graf((cur_chr > 0)); +} + +@ @c +static void run_start_par (void) { + if (cur_chr != 2) + indent_in_hmode(); +} + +@ @c +static void run_new_graf (void) { + back_input(); + new_graf(true); +} + +@ A paragraph ends when a |par_end| command is sensed, or when we are in +horizontal mode when reaching the right brace of vertical-mode routines +like \.{\\vbox}, \.{\\insert}, or \.{\\output}. + +@c +static void run_par_end_vmode (void) { + normal_paragraph(); + if (mode > 0) { + checked_page_filter(vmode_par); + build_page(); + } +} + +@ @c +static void run_par_end_hmode (void) { + if (align_state < 0) + off_save(); /* this tries to recover from an alignment that didn't end properly */ + end_graf(bottom_level); /* this takes us to the enclosing mode, if |mode>0| */ + if (mode == vmode) { + checked_page_filter(hmode_par); + build_page(); + } +} + +@ @c +static void append_italic_correction_mmode (void) { + tail_append(new_kern(0)); /* what subtype to use */ +} + +@ @c +static void run_local_box (void) { + append_local_box(cur_chr); +} + +@ @c +static void run_halign_mmode (void) { + if (privileged()) { + if (cur_group == math_shift_group) + init_align(); + else + off_save(); + } +} + +@ @c +static void run_eq_no (void) { + if (privileged()) { + if (cur_group == math_shift_group) + start_eq_no(); + else + off_save(); + } +} + +@ @c +static void run_letter_mmode (void) { + set_math_char(get_math_code(cur_chr)); +} + +@ @c +static void run_char_num_mmode (void) { + scan_char_num(); + cur_chr = cur_val; + set_math_char(get_math_code(cur_chr)); +} + +@ @c +static void run_math_char_num_mmode (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else + mval = scan_mathchar(umathnum_mathcode); + set_math_char(mval); +} + +@ @c +static void run_math_given_mmode (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + mval = mathchar_from_integer(cur_chr, tex_mathcode); + set_math_char(mval); +} + +static void run_xmath_given_mmode (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + mval = mathchar_from_integer(cur_chr, umath_mathcode); + set_math_char(mval); +} + +@ @c +static void run_delim_num (void) { + mathcodeval mval; /* to build up an argument to |set_math_char| */ + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else + mval = scan_delimiter_as_mathchar(umath_mathcode); + set_math_char(mval); + +} + +@ @c +static void run_vcenter (void) { + scan_spec(vcenter_group); + normal_paragraph(); + push_nest(); + mode = -vmode; + prev_depth_par = ignore_depth; + if (every_vbox_par != null) + begin_token_list(every_vbox_par, every_vbox_text); +} + +@ @c +static void run_math_style (void) { + tail_append(new_style((small_number) cur_chr)); +} + +@ @c +static void run_non_script (void) { + tail_append(new_glue(zero_glue)); + subtype(tail) = cond_math_glue; +} + +@ @c +static void run_math_choice (void) { + if (cur_chr == 0) + append_choices(); + else + setup_math_style(); +} + +@ @c +static void run_math_shift (void) { + if (cur_group == math_shift_group) + after_math(); + else + off_save(); +} + +@ @c +static void run_after_assignment (void) { + get_token(); + after_token = cur_tok; +} + +@ @c +static void run_after_group (void) { + get_token(); + save_for_after(cur_tok); +} + +@ @c +static void run_extension (void) { + do_extension(0); +} + +static void run_normal (void) { +{ + switch (cur_chr) { + case save_pos_code: + new_whatsit(save_pos_node); + break; + case save_cat_code_table_code: + scan_int(); + if ((cur_val < 0) || (cur_val > 0x7FFF)) { + print_err("Invalid \\catcode table"); + help1("All \\catcode table ids must be between 0 and 0x7FFF"); + error(); + } else { + if (cur_val == cat_code_table_par) { + print_err("Invalid \\catcode table"); + help1("You cannot overwrite the current \\catcode table"); + error(); + } else { + copy_cat_codes(cat_code_table_par, cur_val); + } + } + break; + case init_cat_code_table_code: + scan_int(); + if ((cur_val < 0) || (cur_val > 0x7FFF)) { + print_err("Invalid \\catcode table"); + help1("All \\catcode table ids must be between 0 and 0x7FFF"); + error(); + } else { + if (cur_val == cat_code_table_par) { + print_err("Invalid \\catcode table"); + help1("You cannot overwrite the current \\catcode table"); + error(); + } else { + initex_cat_codes(cur_val); + } + } + break; + case set_random_seed_code: + /* Negative random seed values are silently converted to positive ones */ + scan_int(); + if (cur_val < 0) + negate(cur_val); + random_seed = cur_val; + init_randoms(random_seed); + break; + case late_lua_code: + new_whatsit(late_lua_node); /* type == normal */ + late_lua_name(tail) = scan_lua_state(); + (void) scan_toks(false, false); + late_lua_data(tail) = def_ref; + break; + case expand_font_code: + read_expand_font(); + break; + default: + confusion("int1"); + break; + } + } +} + +/* + this is experimental and not used for production, only for testing and writing + macros (some options stay) + +*/ + +#define mathoption_set_int(A) \ + scan_int(); \ + word_define(mathoption_int_base+A, cur_val); + +static void run_option(void) { + int a = 0 ; + switch (cur_chr) { + case math_option_code: + if (scan_keyword("old")) { + mathoption_set_int(c_mathoption_old_code); + } else if (scan_keyword("noitaliccompensation")) { + mathoption_set_int(c_mathoption_no_italic_compensation_code); + } else if (scan_keyword("nocharitalic")) { + mathoption_set_int(c_mathoption_no_char_italic_code); + } else if (scan_keyword("useoldfractionscaling")) { + mathoption_set_int(c_mathoption_use_old_fraction_scaling_code); + } else if (scan_keyword("umathcodemeaning")) { + mathoption_set_int(c_mathoption_umathcode_meaning_code); + } else { + normal_warning("mathoption","unknown key"); + } + break; + default: + /* harmless */ + break; + } +} + +@ For mode-independent commands, the following macro is useful. + +Also, there is a list of cases where the user has probably gotten into or out of math +mode by mistake. \TeX\ will insert a dollar sign and rescan the current token, and +it makes sense ot have a macro for that as well. + +@c +#define any_mode(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; jump_table[mmode+(A)]=B +#define non_math(A,B) jump_table[vmode+(A)]=B; jump_table[hmode+(A)]=B; + + +@ The |main_control| uses a jump table, and |init_main_control| sets that table up. +@c +typedef void (*main_control_function) (void); +main_control_function *jump_table; + +static void init_main_control (void) { + jump_table = xmalloc((mmode+max_command_cmd+1) * sizeof(main_control_function)) ; + + jump_table[hmode + char_num_cmd] = run_char_num; + jump_table[hmode + letter_cmd] = run_char; + jump_table[hmode + other_char_cmd] = run_char; + jump_table[hmode + char_given_cmd] = run_char; + jump_table[hmode + spacer_cmd] = run_app_space; + jump_table[hmode + ex_space_cmd] = run_app_space; + jump_table[mmode + ex_space_cmd] = run_app_space; + jump_table[hmode + boundary_cmd] = run_boundary; + jump_table[hmode + char_ghost_cmd] = run_char_ghost; + jump_table[mmode + char_ghost_cmd] = run_char_ghost; + any_mode(relax_cmd, run_relax); + jump_table[vmode + spacer_cmd] = run_relax; + jump_table[mmode + spacer_cmd] = run_relax; + jump_table[mmode + boundary_cmd] = run_relax; + any_mode(ignore_spaces_cmd,run_ignore_spaces); + jump_table[vmode + stop_cmd] = run_stop; + jump_table[vmode + math_char_num_cmd] = run_non_math_math; + jump_table[vmode + math_given_cmd] = run_non_math_math; + jump_table[vmode + xmath_given_cmd] = run_non_math_math; + jump_table[hmode + math_char_num_cmd] = run_math_char_num; + jump_table[hmode + math_given_cmd] = run_math_given; + jump_table[hmode + xmath_given_cmd] = run_xmath_given; + + jump_table[vmode + vmove_cmd] = report_illegal_case; + jump_table[hmode + hmove_cmd] = report_illegal_case; + jump_table[mmode + hmove_cmd] = report_illegal_case; + any_mode(last_item_cmd, report_illegal_case); + jump_table[vmode + vadjust_cmd] = report_illegal_case; + jump_table[vmode + ital_corr_cmd] = report_illegal_case; + non_math(eq_no_cmd,report_illegal_case); + any_mode(mac_param_cmd,report_illegal_case); + + non_math(sup_mark_cmd, insert_dollar_sign); + non_math(sub_mark_cmd, insert_dollar_sign); + non_math(super_sub_script_cmd, insert_dollar_sign); + non_math(math_comp_cmd, insert_dollar_sign); + non_math(delim_num_cmd, insert_dollar_sign); + non_math(left_right_cmd, insert_dollar_sign); + non_math(above_cmd, insert_dollar_sign); + non_math(radical_cmd, insert_dollar_sign); + non_math(math_style_cmd, insert_dollar_sign); + non_math(math_choice_cmd, insert_dollar_sign); + non_math(vcenter_cmd, insert_dollar_sign); + non_math(non_script_cmd, insert_dollar_sign); + non_math(mkern_cmd, insert_dollar_sign); + non_math(limit_switch_cmd, insert_dollar_sign); + non_math(mskip_cmd, insert_dollar_sign); + non_math(math_accent_cmd, insert_dollar_sign); + jump_table[mmode + endv_cmd] = insert_dollar_sign; + jump_table[mmode + par_end_cmd] = insert_dollar_sign_par_end; + jump_table[mmode + stop_cmd] = insert_dollar_sign; + jump_table[mmode + vskip_cmd] = insert_dollar_sign; + jump_table[mmode + un_vbox_cmd] = insert_dollar_sign; + jump_table[mmode + valign_cmd] = insert_dollar_sign; + jump_table[mmode + hrule_cmd] = insert_dollar_sign; + jump_table[mmode + no_hrule_cmd] = insert_dollar_sign; + jump_table[vmode + hrule_cmd] = run_rule; + jump_table[vmode + no_hrule_cmd] = run_rule; + jump_table[hmode + vrule_cmd] = run_rule; + jump_table[hmode + no_vrule_cmd] = run_rule; + jump_table[mmode + vrule_cmd] = run_rule; + jump_table[mmode + no_vrule_cmd] = run_rule; + jump_table[vmode + vskip_cmd] = append_glue; + jump_table[hmode + hskip_cmd] = append_glue; + jump_table[mmode + hskip_cmd] = append_glue; + jump_table[mmode + mskip_cmd] = append_glue; + any_mode(kern_cmd, append_kern); + jump_table[mmode + mkern_cmd] = append_kern; + non_math(left_brace_cmd, run_left_brace); + any_mode(begin_group_cmd,run_begin_group); + any_mode(end_group_cmd, run_end_group); + any_mode(right_brace_cmd, handle_right_brace); + jump_table[vmode + hmove_cmd] = run_move; + jump_table[hmode + vmove_cmd] = run_move; + jump_table[mmode + vmove_cmd] = run_move; + any_mode(leader_ship_cmd, run_leader_ship); + any_mode(make_box_cmd, run_make_box); + any_mode(assign_box_dir_cmd, run_box_dir); + jump_table[vmode + start_par_cmd] = run_start_par_vmode; + jump_table[hmode + start_par_cmd] = run_start_par; + jump_table[mmode + start_par_cmd] = run_start_par; + jump_table[vmode + letter_cmd] = run_new_graf; + jump_table[vmode + other_char_cmd] = run_new_graf; + jump_table[vmode + char_num_cmd] = run_new_graf; + jump_table[vmode + char_given_cmd] = run_new_graf; + jump_table[vmode + char_ghost_cmd] = run_new_graf; + jump_table[vmode + math_shift_cmd] = run_new_graf; + jump_table[vmode + math_shift_cs_cmd] = run_new_graf; + jump_table[vmode + un_hbox_cmd] = run_new_graf; + jump_table[vmode + vrule_cmd] = run_new_graf; + jump_table[vmode + no_vrule_cmd] = run_new_graf; + jump_table[vmode + accent_cmd] = run_new_graf; + jump_table[vmode + discretionary_cmd] = run_new_graf; + jump_table[vmode + hskip_cmd] = run_new_graf; + jump_table[vmode + valign_cmd] = run_new_graf; + jump_table[vmode + ex_space_cmd] = run_new_graf; + jump_table[vmode + boundary_cmd] = run_new_graf; + jump_table[vmode + par_end_cmd] = run_par_end_vmode; + jump_table[hmode + par_end_cmd] = run_par_end_hmode; + jump_table[hmode + stop_cmd] = head_for_vmode; + jump_table[hmode + vskip_cmd] = head_for_vmode; + jump_table[hmode + hrule_cmd] = head_for_vmode; + jump_table[hmode + no_hrule_cmd] = head_for_vmode; + jump_table[hmode + un_vbox_cmd] = head_for_vmode; + jump_table[hmode + halign_cmd] = head_for_vmode; + any_mode(insert_cmd,begin_insert_or_adjust); + jump_table[hmode + vadjust_cmd] = begin_insert_or_adjust; + jump_table[mmode + vadjust_cmd] = begin_insert_or_adjust; + any_mode(mark_cmd, handle_mark); + any_mode(break_penalty_cmd, append_penalty); + any_mode(remove_item_cmd, delete_last); + jump_table[vmode + un_vbox_cmd] = unpackage; + jump_table[hmode + un_hbox_cmd] = unpackage; + jump_table[mmode + un_hbox_cmd] = unpackage; + jump_table[hmode + ital_corr_cmd] = append_italic_correction; + jump_table[mmode + ital_corr_cmd] = append_italic_correction_mmode; + jump_table[hmode + discretionary_cmd] = append_discretionary; + jump_table[mmode + discretionary_cmd] = append_discretionary; + any_mode(assign_local_box_cmd, run_local_box); + jump_table[hmode + accent_cmd] = make_accent; + any_mode(car_ret_cmd,align_error); + any_mode(tab_mark_cmd,align_error); + any_mode(no_align_cmd,no_align_error); + any_mode(omit_cmd, omit_error); + jump_table[vmode + halign_cmd] = init_align; + jump_table[hmode + valign_cmd] = init_align; + jump_table[mmode + halign_cmd] = run_halign_mmode; + jump_table[vmode + endv_cmd] = do_endv; + jump_table[hmode + endv_cmd] = do_endv; + any_mode(end_cs_name_cmd, cs_error); + jump_table[hmode + math_shift_cmd] = init_math; + jump_table[hmode + math_shift_cs_cmd] = init_math; + jump_table[mmode + eq_no_cmd] = run_eq_no; + jump_table[mmode + left_brace_cmd] = math_left_brace; + jump_table[mmode + letter_cmd] = run_letter_mmode; + jump_table[mmode + other_char_cmd] = run_letter_mmode; + jump_table[mmode + char_given_cmd] = run_letter_mmode; + jump_table[mmode + char_num_cmd] = run_char_num_mmode; + jump_table[mmode + math_char_num_cmd] = run_math_char_num_mmode; + jump_table[mmode + math_given_cmd] = run_math_given_mmode; + jump_table[mmode + xmath_given_cmd] = run_xmath_given_mmode; + jump_table[mmode + delim_num_cmd] = run_delim_num; + jump_table[mmode + math_comp_cmd] = math_math_comp; + jump_table[mmode + limit_switch_cmd] = math_limit_switch; + jump_table[mmode + radical_cmd] = math_radical; + jump_table[mmode + accent_cmd] = math_ac; + jump_table[mmode + math_accent_cmd] = math_ac; + jump_table[mmode + vcenter_cmd] = run_vcenter; + jump_table[mmode + math_style_cmd] = run_math_style; + jump_table[mmode + non_script_cmd] = run_non_script; + jump_table[mmode + math_choice_cmd] = run_math_choice; + jump_table[mmode + above_cmd] = math_fraction; + jump_table[mmode + sub_mark_cmd] = sub_sup; + jump_table[mmode + sup_mark_cmd] = sub_sup; + jump_table[mmode + super_sub_script_cmd] = sub_sup; + jump_table[mmode + left_right_cmd] = math_left_right; + jump_table[mmode + math_shift_cmd] = run_math_shift; + jump_table[mmode + math_shift_cs_cmd] = run_math_shift; + any_mode(toks_register_cmd, prefixed_command); + any_mode(assign_toks_cmd, prefixed_command); + any_mode(assign_int_cmd, prefixed_command); + any_mode(assign_attr_cmd, prefixed_command); + any_mode(assign_dir_cmd, prefixed_command); + any_mode(assign_dimen_cmd, prefixed_command); + any_mode(assign_glue_cmd, prefixed_command); + any_mode(assign_mu_glue_cmd, prefixed_command); + any_mode(assign_font_dimen_cmd, prefixed_command); + any_mode(assign_font_int_cmd, prefixed_command); + any_mode(set_aux_cmd, prefixed_command); + any_mode(set_prev_graf_cmd, prefixed_command); + any_mode(set_page_dimen_cmd, prefixed_command); + any_mode(set_page_int_cmd, prefixed_command); + any_mode(set_box_dimen_cmd, prefixed_command); + any_mode(set_tex_shape_cmd, prefixed_command); + any_mode(set_etex_shape_cmd, prefixed_command); + any_mode(def_char_code_cmd, prefixed_command); + any_mode(def_del_code_cmd, prefixed_command); + any_mode(extdef_math_code_cmd, prefixed_command); + any_mode(extdef_del_code_cmd, prefixed_command); + any_mode(def_family_cmd, prefixed_command); + any_mode(set_math_param_cmd, prefixed_command); + any_mode(set_font_cmd, prefixed_command); + any_mode(def_font_cmd, prefixed_command); + any_mode(letterspace_font_cmd, prefixed_command); + any_mode(copy_font_cmd, prefixed_command); + any_mode(set_font_id_cmd, prefixed_command); + any_mode(register_cmd, prefixed_command); + any_mode(advance_cmd, prefixed_command); + any_mode(multiply_cmd, prefixed_command); + any_mode(divide_cmd, prefixed_command); + any_mode(prefix_cmd, prefixed_command); + any_mode(let_cmd, prefixed_command); + any_mode(shorthand_def_cmd, prefixed_command); + any_mode(read_to_cs_cmd, prefixed_command); + any_mode(def_cmd, prefixed_command); + any_mode(set_box_cmd, prefixed_command); + any_mode(hyph_data_cmd, prefixed_command); + any_mode(set_interaction_cmd, prefixed_command); + any_mode(after_assignment_cmd,run_after_assignment); + any_mode(after_group_cmd,run_after_group); + any_mode(in_stream_cmd,open_or_close_in); + any_mode(message_cmd,issue_message); + any_mode(case_shift_cmd, shift_case); + any_mode(xray_cmd, show_whatever); + any_mode(normal_cmd, run_normal); + any_mode(extension_cmd, run_extension); + any_mode(option_cmd, run_option); +} + +@ And here is |main_control| itself. It is quite short nowadays. + +@c +void main_control(void) +{ + main_control_state = goto_next; + init_main_control () ; + + if (equiv(every_job_loc) != null) + begin_token_list(equiv(every_job_loc), every_job_text); + + while (1) { + if (main_control_state == goto_skip_token) + main_control_state = goto_next; /* reset */ + else + get_x_token(); + + /* Give diagnostic information, if requested */ + /* When a new token has just been fetched at |big_switch|, we have an + ideal place to monitor \TeX's activity. */ + if (interrupt != 0 && OK_to_interrupt) { + back_input(); + check_interrupt(); + continue; + } + if (tracing_commands_par > 0) + show_cur_cmd_chr(); + + (jump_table[(abs(mode) + cur_cmd)])(); /* run the command */ + + if (main_control_state == goto_return) { + return; + } + } + return; /* not reached */ +} + +@ @c +void app_space(void) +{ /* handle spaces when |space_factor<>1000| */ + halfword q; /* glue node */ + if ((space_factor_par >= 2000) && (! glue_is_zero(xspace_skip_par))) { + q = new_param_glue(xspace_skip_code); + /* so from now we have a subtype with spaces: */ + subtype(q) = xspace_skip_code + 1; + } else { + if (!glue_is_zero(space_skip_par)) { + q = new_glue(space_skip_par); + } else { + q = new_glue(zero_glue); + width(q) = space(cur_font_par); + stretch(q) = space_stretch(cur_font_par); + shrink(q) = space_shrink(cur_font_par); + } + /* Modify the glue specification in |q| according to the space factor */ + if (space_factor_par >= 2000) + width(q) = width(q) + extra_space(cur_font_par); + stretch(q) = xn_over_d(stretch(q), space_factor_par, 1000); + shrink(q) = xn_over_d(shrink(q), 1000, space_factor_par); + + /* so from now we have a subtype with spaces: */ + subtype(q) = space_skip_code + 1; + } + couple_nodes(tail, q); + tail = q; +} + +@ @c +void insert_dollar_sign(void) +{ + back_input(); + cur_tok = math_shift_token + '$'; + print_err("Missing $ inserted"); + help2("I've inserted a begin-math/end-math symbol since I think", + "you left one out. Proceed, with fingers crossed."); + ins_error(); +} + +@ We can silently ignore \.{\\par}s in a math formula. + +@c +void insert_dollar_sign_par_end(void) +{ + if (!suppress_mathpar_error_par) { + insert_dollar_sign() ; + } +} + +@ The `|you_cant|' procedure prints a line saying that the current command +is illegal in the current mode; it identifies these things symbolically. + +@c +void you_cant(void) +{ + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_in_mode(mode); +} + +@ +When erroneous situations arise, \TeX\ usually issues an error message +specific to the particular error. For example, `\.{\\noalign}' should +not appear in any mode, since it is recognized by the |align_peek| routine +in all of its legitimate appearances; a special error message is given +when `\.{\\noalign}' occurs elsewhere. But sometimes the most appropriate +error message is simply that the user is not allowed to do what he or she +has attempted. For example, `\.{\\moveleft}' is allowed only in vertical mode, +and `\.{\\lower}' only in non-vertical modes. Such cases are enumerated +here and in the other sections referred to under `See also \dots.' + +@c +void report_illegal_case(void) +{ + you_cant(); + help4("Sorry, but I'm not programmed to handle this case;", + "I'll just pretend that you didn''t ask for it.", + "If you're in the wrong mode, you might be able to", + "return to the right one by typing `I}' or `I$' or `I\\par'."); + error(); +} + +@ Some operations are allowed only in privileged modes, i.e., in cases +that |mode>0|. The |privileged| function is used to detect violations +of this rule; it issues an error message and returns |false| if the +current |mode| is negative. + +@c +boolean privileged(void) +{ + if (mode > 0) { + return true; + } else { + report_illegal_case(); + return false; + } +} + +@ We don't want to leave |main_control| immediately when a |stop| command +is sensed, because it may be necessary to invoke an \.{\\output} routine +several times before things really grind to a halt. (The output routine +might even say `\.{\\gdef\\end\{...\}}', to prolong the life of the job.) +Therefore |its_all_over| is |true| only when the current page +and contribution list are empty, and when the last output was not a +``dead cycle.'' + +@c +boolean its_all_over(void) +{ /* do this when \.{\\end} or \.{\\dump} occurs */ + if (privileged()) { + if ((page_head == page_tail) && (head == tail) && (dead_cycles == 0)) { + return true; + } + back_input(); /* we will try to end again after ejecting residual material */ + tail_append(new_null_box()); + width(tail) = hsize_par; + tail_append(new_glue(fill_glue)); + tail_append(new_penalty(-010000000000,final_penalty)); + normal_page_filter(end); + build_page(); /* append \.{\\hbox to \\hsize\{\}\\vfill\\penalty-'10000000000} */ + } + return false; +} + + +@ The |hskip| and |vskip| command codes are used for control sequences +like \.{\\hss} and \.{\\vfil} as well as for \.{\\hskip} and \.{\\vskip}. +The difference is in the value of |cur_chr|. + +All the work relating to glue creation has been relegated to the +following subroutine. It does not call |build_page|, because it is +used in at least one place where that would be a mistake. + +@c +void append_glue(void) +{ + int s = cur_chr; + switch (s) { + case fil_code: + cur_val = new_glue(fil_glue); + break; + case fill_code: + cur_val = new_glue(fill_glue); + break; + case ss_code: + cur_val = new_glue(ss_glue); + break; + case fil_neg_code: + cur_val = new_glue(fil_neg_glue); + break; + case skip_code: + scan_glue(glue_val_level); + break; + case mskip_code: + scan_glue(mu_val_level); + break; + } + /* now |cur_val| points to the glue specification */ + tail_append(new_glue(cur_val)); + flush_node(cur_val); + if (s > skip_code) { + subtype(tail) = mu_glue; + } +} + +@ @c +void append_kern(void) +{ + int s; /* |subtype| of the kern node */ + s = cur_chr; + scan_dimen((s == mu_glue), false, false); + tail_append(new_kern(cur_val)); + subtype(tail) = (quarterword) s; +} + +@ We have to deal with errors in which braces and such things are not +properly nested. Sometimes the user makes an error of commission by +inserting an extra symbol, but sometimes the user makes an error of omission. +\TeX\ can't always tell one from the other, so it makes a guess and tries +to avoid getting into a loop. + +The |off_save| routine is called when the current group code is wrong. It tries +to insert something into the user's input that will help clean off +the top level. + +@c +void off_save(void) +{ + halfword p, q; /* inserted token */ + if (cur_group == bottom_level) { + /* Drop current token and complain that it was unmatched */ + print_err("Extra "); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + help1("Things are pretty mixed up, but I think the worst is over."); + error(); + + } else { + back_input(); + p = get_avail(); + set_token_link(temp_token_head, p); + print_err("Missing "); + /* Prepare to insert a token that matches |cur_group|, and print what it is */ + /* At this point, |link(temp_token_head)=p|, a pointer to an empty one-word node. */ + switch (cur_group) { + case semi_simple_group: + set_token_info(p, cs_token_flag + frozen_end_group); + tprint_esc("endgroup"); + break; + case math_shift_group: + set_token_info(p, math_shift_token + '$'); + print_char('$'); + break; + case math_left_group: + set_token_info(p, cs_token_flag + frozen_right); + q = get_avail(); + set_token_link(p, q); + p = token_link(p); + set_token_info(p, other_token + '.'); + tprint_esc("right."); + break; + default: + set_token_info(p, right_brace_token + '}'); + print_char('}'); + break; + } + tprint(" inserted"); + ins_list(token_link(temp_token_head)); + help5("I've inserted something that you may have forgotten.", + "(See the above.)", + "With luck, this will get me unwedged. But if you", + "really didn't forget anything, try typing `2' now; then", + "my insertion and my current dilemma will both disappear."); + error(); + } +} + +@ The routine for a |right_brace| character branches into many subcases, +since a variety of things may happen, depending on |cur_group|. Some +types of groups are not supposed to be ended by a right brace; error +messages are given in hopes of pinpointing the problem. Most branches +of this routine will be filled in later, when we are ready to understand +them; meanwhile, we must prepare ourselves to deal with such errors. + +@c +void handle_right_brace(void) +{ + halfword p, q; /* for short-term use */ + scaled d; /* holds |split_max_depth| in |insert_group| */ + int f; /* holds |floating_penalty| in |insert_group| */ + p = null; + switch (cur_group) { + case simple_group: + fixup_directions(); + break; + case bottom_level: + print_err("Too many }'s"); + help2("You've closed more groups than you opened.", + "Such booboos are generally harmless, so keep going."); + error(); + break; + case semi_simple_group: + case math_shift_group: + case math_left_group: + extra_right_brace(); + break; + case hbox_group: + /* When the right brace occurs at the end of an \.{\\hbox} or \.{\\vbox} or + \.{\\vtop} construction, the |package| routine comes into action. We might + also have to finish a paragraph that hasn't ended. */ + package(0); + break; + case adjusted_hbox_group: + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + package(0); + break; + case vbox_group: + end_graf(vbox_group); + package(0); + break; + case vtop_group: + end_graf(vtop_group); + package(vtop_code); + break; + case insert_group: + end_graf(insert_group); + q = new_glue(split_top_skip_par); + d = split_max_depth_par; + f = floating_penalty_par; + unsave(); + save_ptr--; + /* now |saved_value(0)| is the insertion number, or the |vadjust| subtype */ + p = vpack(vlink(head), 0, additional, -1); + pop_nest(); + if (saved_type(0) == saved_insert) { + tail_append(new_node(ins_node, saved_value(0))); + height(tail) = height(p) + depth(p); + ins_ptr(tail) = list_ptr(p); + split_top_ptr(tail) = q; + depth(tail) = d; + float_cost(tail) = f; + } else if (saved_type(0) == saved_adjust) { + tail_append(new_node(adjust_node, saved_value(0))); + adjust_ptr(tail) = list_ptr(p); + flush_node(q); + } else { + confusion("insert_group"); + } + list_ptr(p) = null; + flush_node(p); + if (nest_ptr == 0) { + checked_page_filter(insert); + build_page(); + } + break; + case output_group: + /* this is needed in case the \.{\\output} executes a \.{\\textdir} command. */ + if (dir_level(text_dir_ptr) == cur_level) { + /* DIR: Remove from |text_dir_ptr| */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + resume_after_output(); + break; + case disc_group: + build_discretionary(); + break; + case local_box_group: + build_local_box(); + break; + case align_group: + back_input(); + cur_tok = cs_token_flag + frozen_cr; + print_err("Missing \\cr inserted"); + help1("I'm guessing that you meant to end an alignment here."); + ins_error(); + break; + case no_align_group: + end_graf(no_align_group); + unsave(); + align_peek(); + break; + case vcenter_group: + end_graf(vcenter_group); + finish_vcenter(); + break; + case math_choice_group: + build_choices(); + break; + case math_group: + close_math_group(p); + break; + default: + confusion("rightbrace"); + break; + } +} + +@ @c +void extra_right_brace(void) +{ + print_err("Extra }, or forgotten "); + switch (cur_group) { + case semi_simple_group: + tprint_esc("endgroup"); + break; + case math_shift_group: + print_char('$'); + break; + case math_left_group: + tprint_esc("right"); + break; + } + help5("I've deleted a group-closing symbol because it seems to be", + "spurious, as in `$x}$'. But perhaps the } is legitimate and", + "you forgot something else, as in `\\hbox{$x}'. In such cases", + "the way to recover is to insert both the forgotten and the", + "deleted material, e.g., by typing `I$}'."); + error(); + incr(align_state); +} + +@ Here is where we clear the parameters that are supposed to revert to their +default values after every paragraph and when internal vertical mode is entered. + +@c +void normal_paragraph(void) +{ + if (looseness_par != 0) + eq_word_define(int_base + looseness_code, 0); + if (hang_indent_par != 0) + eq_word_define(dimen_base + hang_indent_code, 0); + if (hang_after_par != 1) + eq_word_define(int_base + hang_after_code, 1); + if (par_shape_par_ptr != null) + eq_define(par_shape_loc, shape_ref_cmd, null); + if (inter_line_penalties_par_ptr != null) + eq_define(inter_line_penalties_loc, shape_ref_cmd, null); + if (shape_mode_par > 0) + eq_word_define(dimen_base + shape_mode_code, 0); +} + +@ The global variable |cur_box| will point to a newly-made box. If the box +is void, we will have |cur_box=null|. Otherwise we will have +|type(cur_box)=hlist_node| or |vlist_node| or |rule_node|; the |rule_node| +case can occur only with leaders. + +@c +halfword cur_box; /* box to be placed into its context */ + +@ The |box_end| procedure does the right thing with |cur_box|, if +|box_context| represents the context as explained above. + +@c +void box_end(int box_context) +{ + if (box_context < box_flag) { + /* Append box |cur_box| to the current list, shifted by |box_context| */ + /* + The global variable |adjust_tail| will be non-null if and only if the + current box might include adjustments that should be appended to the + current vertical list. + */ + if (cur_box != null) { + shift_amount(cur_box) = box_context; + if (abs(mode) == vmode) { + if (pre_adjust_tail != null) { + if (pre_adjust_head != pre_adjust_tail) + append_list(pre_adjust_head, pre_adjust_tail); + pre_adjust_tail = null; + } + append_to_vlist(cur_box,lua_key_index(box)); + if (adjust_tail != null) { + if (adjust_head != adjust_tail) + append_list(adjust_head, adjust_tail); + adjust_tail = null; + } + if (mode > 0) { + checked_page_filter(box); + build_page(); + } + } else { + if (abs(mode) == hmode) + space_factor_par = 1000; + else + cur_box = new_sub_box(cur_box); + couple_nodes(tail, cur_box); + tail = cur_box; + } + } + } else if (box_context < ship_out_flag) { + /* Store |cur_box| in a box register */ + if (box_context < global_box_flag) + eq_define(box_base + box_context - box_flag, box_ref_cmd, cur_box); + else + geq_define(box_base + box_context - global_box_flag, box_ref_cmd, cur_box); + } else if (cur_box != null) { + if (box_context > ship_out_flag) { + /* Append a new leader node that uses |cur_box| */ + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (((cur_cmd == hskip_cmd) && (abs(mode) != vmode)) || + ((cur_cmd == vskip_cmd) && (abs(mode) == vmode))) { + append_glue(); + subtype(tail) = (quarterword) (box_context - (leader_flag - a_leaders)); + leader_ptr(tail) = cur_box; + } else { + print_err("Leaders not followed by proper glue"); + help3 + ("You should say `\\leaders '.", + "I found the , but there's no suitable", + ", so I'm ignoring these leaders."); + back_error(); + flush_node_list(cur_box); + } + } else { + ship_out(static_pdf, cur_box, SHIPPING_PAGE); + } + } +} + +@ the next input should specify a box or perhaps a rule + +@c +void scan_box(int box_context) +{ + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd == make_box_cmd) { + begin_box(box_context); + } else if ((box_context >= leader_flag) && + ((cur_cmd == hrule_cmd) || (cur_cmd == vrule_cmd) || + (cur_cmd == no_hrule_cmd) || (cur_cmd == no_vrule_cmd))) { + cur_box = scan_rule_spec(); + box_end(box_context); + } else { + print_err("A was supposed to be here"); + help3("I was expecting to see \\hbox or \\vbox or \\copy or \\box or", + "something like that. So you might find something missing in", + "your output. But keep trying; you can fix this later."); + back_error(); + } +} + +@ @c +void new_graf(boolean indented) +{ + halfword p, q, dir_graf_tmp; + halfword dir_rover; + prev_graf_par = 0; + if ((mode == vmode) || (head != tail)) { + tail_append(new_param_glue(par_skip_code)); + } + push_nest(); + mode = hmode; + space_factor_par = 1000; + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node(new_graf_par_code)); + if (indented) { + p = new_null_box(); + box_dir(p) = par_direction_par; + width(p) = par_indent_par; + subtype(p) = indent_list; + q = tail; + tail_append(p); + } else { + q = tail; + } + dir_rover = text_dir_ptr; + while (dir_rover != null) { + if ((vlink(dir_rover) != null) || (dir_dir(dir_rover) != par_direction_par)) { + dir_graf_tmp = new_dir(dir_dir(dir_rover)); + try_couple_nodes(dir_graf_tmp,vlink(q)); + couple_nodes(q,dir_graf_tmp); + } + dir_rover = vlink(dir_rover); + } + q = head; + while (vlink(q) != null) + q = vlink(q); + tail = q; + if (every_par_par != null) + begin_token_list(every_par_par, every_par_text); + if (nest_ptr == 1) { + checked_page_filter(new_graf); + build_page(); /* put |par_skip| glue on current page */ + } +} + +@ @c +void indent_in_hmode(void) +{ + halfword p; + if (cur_chr > 0) { /* \.{\\indent} */ + p = new_null_box(); + width(p) = par_indent_par; + if (abs(mode) == hmode) + space_factor_par = 1000; + else + p = new_sub_box(p); + tail_append(p); + } +} + +@ @c +void head_for_vmode(void) +{ + if (mode < 0) { + if ((cur_cmd != hrule_cmd) && (cur_cmd != no_hrule_cmd)) { + off_save(); + } else { + print_err("You can't use `\\hrule' here except with leaders"); + help2("To put a horizontal rule in an hbox or an alignment,", + "you should use \\leaders or \\hrulefill (see The TeXbook)."); + error(); + } + } else { + back_input(); + cur_tok = par_token; + back_input(); + token_type = inserted; + } +} + +@ TODO (BUG?): |dir_save| would have been set by |line_break| by means +of |post_line_break|, but this is not done right now, as it introduces +pretty heavy memory leaks. This means the current code is probably +wrong in some way that relates to in-paragraph displays. + +@c +void end_graf(int line_break_context) +{ + if (mode == hmode) { + if ((head == tail) || (vlink(head) == tail)) { + if (vlink(head) == tail) + flush_node(vlink(head)); + pop_nest(); /* null paragraphs are ignored, all contain a |local_paragraph| node */ + } else { + line_break(false, line_break_context); + } + if (dir_save != null) { + flush_node_list(dir_save); + dir_save = null; + } + normal_paragraph(); + error_count = 0; + } +} + +@ @c +void begin_insert_or_adjust(void) +{ + if (cur_cmd != vadjust_cmd) { + scan_register_num(); + if (cur_val == output_box_par) { + print_err("You can't \\insert"); + print_int(output_box_par); + help1("I'm changing to \\insert0; box \\outputbox is special."); + error(); + cur_val = 0; + } + set_saved_record(0, saved_insert, 0, cur_val); + } else if (scan_keyword("pre")) { + set_saved_record(0, saved_adjust, 0, 1); + } else { + set_saved_record(0, saved_adjust, 0, 0); + } + save_ptr++; + new_save_level(insert_group); + scan_left_brace(); + normal_paragraph(); + push_nest(); + mode = -vmode; + prev_depth_par = ignore_depth; +} + +@ I (TH)'ve renamed the |make_mark| procedure to this, because if the +current chr code is 1, then the actual command was \.{\\clearmarks}, +which does not generate a mark node but instead destroys the current +mark tokenlists. + +@c +void handle_mark(void) +{ + halfword p; /* new node */ + halfword c; /* the mark class */ + if (cur_chr == clear_marks_code) { + scan_mark_num(); + c = cur_val; + delete_top_mark(c); + delete_bot_mark(c); + delete_first_mark(c); + delete_split_first_mark(c); + delete_split_bot_mark(c); + } else { + if (cur_chr == 0) { + c = 0; + } else { + scan_mark_num(); + c = cur_val; + if (c > biggest_used_mark) + biggest_used_mark = c; + } + p = scan_toks(false, true); + p = new_node(mark_node, 0); /* the |subtype| is not used */ + mark_class(p) = c; + mark_ptr(p) = def_ref; + couple_nodes(tail, p); + tail = p; + } +} + +@ @c +void append_penalty(void) +{ + scan_int(); + tail_append(new_penalty(cur_val,user_penalty)); + if (mode == vmode) { + checked_page_filter(penalty); + build_page(); + } +} + +@ When |delete_last| is called, |cur_chr| is the |type| of node that +will be deleted, if present. + +The |remove_item| command removes a penalty, kern, or glue node if it +appears at the tail of the current list, using a brute-force linear scan. +Like \.{\\lastbox}, this command is not allowed in vertical mode (except +internal vertical mode), since the current list in vertical mode is sent +to the page builder. But if we happen to be able to implement it in +vertical mode, we do. + +@c +void delete_last(void) +{ + halfword p, q; /* run through the current list */ + if ((mode == vmode) && (tail == head)) { + /* Apologize for inability to do the operation now, + unless \.{\\unskip} follows non-glue */ + if ((cur_chr != glue_node) || (last_glue != max_halfword)) { + you_cant(); + if (cur_chr == kern_node) { + help2 + ("Sorry...I usually can't take things from the current page.", + "Try `I\\kern-\\lastkern' instead."); + } else if (cur_chr != glue_node) { + help2 + ("Sorry...I usually can't take things from the current page.", + "Perhaps you can make the output routine do it."); + } else { + help2 + ("Sorry...I usually can't take things from the current page.", + "Try `I\\vskip-\\lastskip' instead."); + } + error(); + } + } else { + /* todo: clean this up */ + if (!is_char_node(tail)) { + if (type(tail) == cur_chr) { + q = head; + do { + p = q; + if (!is_char_node(q)) { + if (type(q) == disc_node) { + if (p == tail) + return; + } + } + q = vlink(p); + } while (q != tail); + vlink(p) = null; + flush_node_list(tail); + tail = p; + } + } + } +} + +@ @c +void unpackage(void) +{ + halfword p; /* the box */ + halfword r; /* to remove marginal kern nodes */ + int c; /* should we copy? */ + halfword s; /* for varmem assignment */ + if (cur_chr > copy_code) { + /* Handle saved items and |goto done| */ + try_couple_nodes(tail, disc_ptr[cur_chr]); + disc_ptr[cur_chr] = null; + goto DONE; + } + c = cur_chr; + scan_register_num(); + p = box(cur_val); + if (p == null) + return; + if ((abs(mode) == mmode) + || ((abs(mode) == vmode) && (type(p) != vlist_node)) + || ((abs(mode) == hmode) && (type(p) != hlist_node))) { + print_err("Incompatible list can't be unboxed"); + help3("Sorry, Pandora. (You sneaky devil.)", + "I refuse to unbox an \\hbox in vertical mode or vice versa.", + "And I can't open any boxes in math mode."); + error(); + return; + } + if (c == copy_code) { + s = copy_node_list(list_ptr(p)); + try_couple_nodes(tail,s); + } else { + try_couple_nodes(tail,list_ptr(p)); + box(cur_val) = null; + list_ptr(p) = null; + flush_node(p); + } + DONE: + while (vlink(tail) != null) { + r = vlink(tail); + if (!is_char_node(r) && (type(r) == margin_kern_node)) { + try_couple_nodes(tail,vlink(r)); + flush_node(r); + } + tail = vlink(tail); + } +} + +@ +Italic corrections are converted to kern nodes when the |ital_corr| command +follows a character. In math mode the same effect is achieved by appending +a kern of zero here, since italic corrections are supplied later. + +@c +void append_italic_correction(void) +{ + halfword p; /* |char_node| at the tail of the current list */ + internal_font_number f; /* the font in the |char_node| */ + if (tail != head) { + if (is_char_node(tail)) + p = tail; + else + return; + f = font(p); + tail_append(new_kern(char_italic(f, character(p)))); + subtype(tail) = italic_kern; + } +} + +@ @c +void append_local_box(int kind) +{ + incr(save_ptr); + set_saved_record(-1, saved_boxtype, 0, kind); + new_save_level(local_box_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor_par = 1000; +} + +@ Discretionary nodes are easy in the common case `\.{\\-}', but in the +general case we must process three braces full of items. + +The space factor does not change when we append a discretionary node, +but it starts out as 1000 in the subsidiary lists. + +@c +void append_discretionary(void) +{ + int c; + tail_append(new_disc()); + subtype(tail) = (quarterword) cur_chr; + if (cur_chr == explicit_disc) { + /* \- */ + c = get_pre_hyphen_char(cur_lang_par); + if (c != 0) { + vlink(pre_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(pre_break(tail))) = pre_break(tail); + tlink(pre_break(tail)) = vlink(pre_break(tail)); + } + c = get_post_hyphen_char(cur_lang_par); + if (c != 0) { + vlink(post_break(tail)) = new_char(equiv(cur_font_loc), c); + alink(vlink(post_break(tail))) = post_break(tail); + tlink(post_break(tail)) = vlink(post_break(tail)); + } + set_explicit_disc_penalty(tail); + } else { + /* \discretionary */ + if (scan_keyword("penalty")) { + scan_int(); + disc_penalty(tail) = cur_val; + } + incr(save_ptr); + set_saved_record(-1, saved_disc, 0, 0); + new_save_level(disc_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor_par = 1000; + /* already preset: disc_penalty(tail) = hyphen_penalty_par; */ + } +} + +@ The test for |p != null| ensures that empty \.{\\localleftbox} and + \.{\\localrightbox} commands are not applied. + +@c +void build_local_box(void) +{ + halfword p; + int kind; + unsave(); + assert(saved_type(-1) == saved_boxtype); + kind = saved_value(-1); + decr(save_ptr); + p = vlink(head); + pop_nest(); + if (p != null) + p = hpack(p, 0, additional, -1); + if (kind == 0) + eq_define(local_left_box_base, box_ref_cmd, p); + else + eq_define(local_right_box_base, box_ref_cmd, p); + if (abs(mode) == hmode) { + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node(local_box_par_code)); + } + eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); +} + +@ The three discretionary lists are constructed somewhat as if they were +hboxes. A~subroutine called |build_discretionary| handles the transitions. +(This is sort of fun.) + +@c +void build_discretionary(void) +{ + halfword p, q; /* for link manipulation */ + int n; /* length of discretionary list */ + unsave(); + /* Prune the current list, if necessary, until it contains only + |char_node|, |kern_node|, |hlist_node|, |vlist_node| and + |rule_node| items; set |n| to the length of the list, + and set |q| to the lists tail */ + /* During this loop, |p=vlink(q)| and there are |n| items preceding |p|. */ + q = head; + p = vlink(q); + n = 0; + while (p != null) { + if (!is_char_node(p) && type(p) > rule_node && type(p) != kern_node) { + print_err("Improper discretionary list"); + help1("Discretionary lists must contain only boxes and kerns."); + error(); + begin_diagnostic(); + tprint_nl("The following discretionary sublist has been deleted:"); + show_box(p); + end_diagnostic(true); + flush_node_list(p); + vlink(q) = null; + break; + } + alink(p) = q; + q = p; + p = vlink(q); + incr(n); + } + + p = vlink(head); + pop_nest(); + assert(saved_type(-1) == saved_disc); + switch (saved_value(-1)) { + case 0: + if (n > 0) { + vlink(pre_break(tail)) = p; + alink(p) = pre_break(tail); + tlink(pre_break(tail)) = q; + } + break; + case 1: + if (n > 0) { + vlink(post_break(tail)) = p; + alink(p) = post_break(tail); + tlink(post_break(tail)) = q; + } + break; + case 2: + /* Attach list |p| to the current list, and record its length; + then finish up and |return| */ + if ((n > 0) && (abs(mode) == mmode)) { + print_err("Illegal math \\discretionary"); + help2("Sorry: The third part of a discretionary break must be", + "empty, in math formulas. I had to delete your third part."); + flush_node_list(p); + error(); + } else { + if (n > 0) { + vlink(no_break(tail)) = p; + alink(p) = no_break(tail); + tlink(no_break(tail)) = q; + } + } + decr(save_ptr); + return; + break; + } /* there are no other cases */ + set_saved_record(-1, saved_disc, 0, (saved_value(-1) + 1)); + new_save_level(disc_group); + scan_left_brace(); + push_nest(); + mode = -hmode; + space_factor_par = 1000; +} + +@ The positioning of accents is straightforward but tedious. Given an accent +of width |a|, designed for characters of height |x| and slant |s|; +and given a character of width |w|, height |h|, and slant |t|: We will shift +the accent down by |x-h|, and we will insert kern nodes that have the effect of +centering the accent over the character and shifting the accent to the +right by $\delta={1\over2}(w-a)+h\cdot t-x\cdot s$. If either character is +absent from the font, we will simply use the other, without shifting. + +@c +void make_accent(void) +{ + double s, t; /* amount of slant */ + halfword p, q, r; /* character, box, and kern nodes */ + internal_font_number f; /* relevant font */ + scaled a, h, x, w, delta; /* heights and widths, as explained above */ + scan_char_num(); + f = equiv(cur_font_loc); + p = new_glyph(f, cur_val); + if (p != null) { + x = x_height(f); + s = float_cast(slant(f)) / float_constant(65536); /* real division */ + a = glyph_width(p); + do_assignments(); + /* Create a character node |q| for the next character, + but set |q:=null| if problems arise */ + q = null; + f = equiv(cur_font_loc); + if ((cur_cmd == letter_cmd) || + (cur_cmd == other_char_cmd) || (cur_cmd == char_given_cmd)) { + q = new_glyph(f, cur_chr); + } else if (cur_cmd == char_num_cmd) { + scan_char_num(); + q = new_glyph(f, cur_val); + } else { + back_input(); + } + + if (q != null) { + /* Append the accent with appropriate kerns, then set |p:=q| */ + /* The kern nodes appended here must be distinguished from other kerns, lest + they be wiped away by the hyphenation algorithm or by a previous line break. + + The two kerns are computed with (machine-dependent) |real| arithmetic, but + their sum is machine-independent; the net effect is machine-independent, + because the user cannot remove these nodes nor access them via \.{\\lastkern}. + */ + t = float_cast(slant(f)) / float_constant(65536); /* real division */ + w = glyph_width(q); + h = glyph_height(q); + if (h != x) { /* the accent must be shifted up or down */ + p = hpack(p, 0, additional, -1); + shift_amount(p) = x - h; + } + delta = round(float_cast(w - a) / float_constant(2) + h * t - x * s); /* real multiplication */ + r = new_kern(delta); + subtype(r) = accent_kern; + couple_nodes(tail, r); + couple_nodes(r, p); + tail = new_kern(-a - delta); + subtype(tail) = accent_kern; + couple_nodes(p, tail); + p = q; + + } + couple_nodes(tail, p); + tail = p; + space_factor_par = 1000; + } +} + +@ When `\.{\\cr}' or `\.{\\span}' or a tab mark comes through the scanner +into |main_control|, it might be that the user has foolishly inserted +one of them into something that has nothing to do with alignment. But it is +far more likely that a left brace or right brace has been omitted, since +|get_next| takes actions appropriate to alignment only when `\.{\\cr}' +or `\.{\\span}' or tab marks occur with |align_state=0|. The following +program attempts to make an appropriate recovery. + +@c +void align_error(void) +{ + if (abs(align_state) > 2) { + /* Express consternation over the fact that no alignment is in progress */ + print_err("Misplaced "); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + if (cur_tok == tab_token + '&') { + help6("I can't figure out why you would want to use a tab mark", + "here. If you just want an ampersand, the remedy is", + "simple: Just type `I\\&' now. But if some right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable."); + } else { + help5("I can't figure out why you would want to use a tab mark", + "or \\cr or \\span just now. If something like a right brace", + "up above has ended a previous alignment prematurely,", + "you're probably due for more error messages, and you", + "might try typing `S' now just to see what is salvageable."); + } + error(); + + } else { + back_input(); + if (align_state < 0) { + print_err("Missing { inserted"); + incr(align_state); + cur_tok = left_brace_token + '{'; + } else { + print_err("Missing } inserted"); + decr(align_state); + cur_tok = right_brace_token + '}'; + } + help3("I've put in what seems to be necessary to fix", + "the current column of the current alignment.", + "Try to go on, since this might almost work."); + ins_error(); + } +} + +@ The help messages here contain a little white lie, since \.{\\noalign} +and \.{\\omit} are allowed also after `\.{\\noalign\{...\}}'. + +@c +void no_align_error(void) +{ + print_err("Misplaced \\noalign"); + help2("I expect to see \\noalign only after the \\cr of", + "an alignment. Proceed, and I'll ignore this case."); + error(); +} + +void omit_error(void) +{ + print_err("Misplaced \\omit"); + help2("I expect to see \\omit only after tab marks or the \\cr of", + "an alignment. Proceed, and I'll ignore this case."); + error(); +} + +@ We've now covered most of the abuses of \.{\\halign} and \.{\\valign}. +Let's take a look at what happens when they are used correctly. + +An |align_group| code is supposed to remain on the |save_stack| +during an entire alignment, until |fin_align| removes it. + +A devious user might force an |endv| command to occur just about anywhere; +we must defeat such hacks. + +@c +void do_endv(void) +{ + base_ptr = input_ptr; + input_stack[base_ptr] = cur_input; + while ((input_stack[base_ptr].index_field != v_template) && + (input_stack[base_ptr].loc_field == null) && + (input_stack[base_ptr].state_field == token_list)) + decr(base_ptr); + if ((input_stack[base_ptr].index_field != v_template) || + (input_stack[base_ptr].loc_field != null) || + (input_stack[base_ptr].state_field != token_list)) + fatal_error("(interwoven alignment preambles are not allowed)"); + /*.interwoven alignment preambles... */ + if (cur_group == align_group) { + end_graf(align_group); + if (fin_col()) + fin_row(); + } else { + off_save(); + } +} + +@ Finally, \.{\\endcsname} is not supposed to get through to |main_control|. + +@c +void cs_error(void) +{ + print_err("Extra \\endcsname"); + help1("I'm ignoring this, since I wasn't doing a \\csname."); + error(); +} + +@ + Assignments to values in |eqtb| can be global or local. Furthermore, a + control sequence can be defined to be `\.{\\long}', `\.{\\protected}', + or `\.{\\outer}', and it might or might not be expanded. The prefixes + `\.{\\global}', `\.{\\long}', `\.{\\protected}', + and `\.{\\outer}' can occur in any order. Therefore we assign binary numeric + codes, making it possible to accumulate the union of all specified prefixes + by adding the corresponding codes. (PASCAL's |set| operations could also + have been used.) + + Every prefix, and every command code that might or might not be prefixed, + calls the action procedure |prefixed_command|. This routine accumulates + a sequence of prefixes until coming to a non-prefix, then it carries out + the command. + +@ If the user says, e.g., `\.{\\global\\global}', the redundancy is +silently accepted. + + +@ The different types of code values have different legal ranges; the +following program is careful to check each case properly. + +@c +#define check_def_code(A) do { \ + if (((cur_val<0)&&(p<(A)))||(cur_val>n)) { \ + print_err("Invalid code ("); \ + print_int(cur_val); \ + if (p<(A)) \ + tprint("), should be in the range 0.."); \ + else \ + tprint("), should be at most "); \ + print_int(n); \ + help1("I'm going to use 0 instead of that illegal code value."); \ + error(); \ + cur_val=0; \ + } \ +} while (0) + +@ @c +/* +halfword swap_hang_indent(halfword indentation, halfword shape_mode) { + if (shape_mode == 1 || shape_mode == 3 || shape_mode == -1 || shape_mode == -3) { + return negate(indentation); + } else { + return indentation; + } +} + +halfword swap_parshape_indent(halfword indentation, halfword width, halfword shape_mode) { + if (shape_mode == 2 || shape_mode == 3 || shape_mode == -2 || shape_mode == -3) { + return hsize_par - width - indentation; + } else { + return indentation; + } +} + +*/ + +void prefixed_command(void) +{ + int a; /* accumulated prefix codes so far */ + internal_font_number f; /* identifies a font */ + halfword j; /* index into a \.{\\parshape} specification */ + halfword p, q; /* for temporary short-term use */ + int n; /* ditto */ + boolean e, check_glue; /* should a definition be expanded? or was \.{\\let} not done? */ + mathcodeval mval; /* for handling of \.{\\mathchardef}s */ + a = 0; + while (cur_cmd == prefix_cmd) { + if (!odd(a / cur_chr)) + a = a + cur_chr; + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd <= max_non_prefixed_command) { + /* Discard erroneous prefixes and |return| */ + print_err("You can't use a prefix with `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help2 + ("I'll pretend you didn't say \\long or \\outer or \\global or", + "\\protected."); + back_error(); + return; + } + if (tracing_commands_par > 2) + show_cur_cmd_chr(); + } + /* Discard the prefixes \.{\\long} and \.{\\outer} if they are irrelevant */ + if (a >= 8) { + j = protected_token; + a = a - 8; + } else { + j = 0; + } + if ((cur_cmd != def_cmd) && ((a % 4 != 0) || (j != 0))) { + print_err("You can't use `\\long' or `\\outer' or `\\protected' with `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + print_char('\''); + help1("I'll pretend you didn't say \\long or \\outer or \\protected here."); + error(); + } + /* Adjust for the setting of \.{\\globaldefs} */ + if (global_defs_par != 0) { + if (global_defs_par < 0) { + if (is_global(a)) + a = a - 4; + } else { + if (!is_global(a)) + a = a + 4; + } + } + switch (cur_cmd) { + case set_font_cmd: + /* Here's an example of the way many of the following routines operate. + (Unfortunately, they aren't all as simple as this.) */ + define(cur_font_loc, data_cmd, cur_chr); + break; + case def_cmd: + /* When a |def| command has been scanned, + |cur_chr| is odd if the definition is supposed to be global, and + |cur_chr>=2| if the definition is supposed to be expanded. */ + + if (odd(cur_chr) && !is_global(a) && (global_defs_par >= 0)) + a = a + 4; + e = (cur_chr >= 2); + get_r_token(); + p = cur_cs; + q = scan_toks(true, e); + if (j != 0) { + q = get_avail(); + set_token_info(q, j); + set_token_link(q, token_link(def_ref)); + set_token_link(def_ref, q); + } + define(p, call_cmd + (a % 4), def_ref); + break; + case let_cmd: + n = cur_chr; + if (n == normal) { + get_r_token(); + p = cur_cs; + do { + get_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '=') { + get_token(); + if (cur_cmd == spacer_cmd) + get_token(); + } + } else if (n == normal + 1) { + /* futurelet */ + get_r_token(); + p = cur_cs; + get_token(); + q = cur_tok; + get_token(); + back_input(); + cur_tok = q; + /* look ahead, then back up */ + /* note that |back_input| doesn't affect |cur_cmd|, |cur_chr| */ + back_input(); + } else { + /* letcharcode */ + scan_int(); + if (cur_val > 0) { + cur_cs = active_to_cs(cur_val, true); + set_token_info(cur_cs, cur_cs + cs_token_flag); + p = cur_cs; + do { + get_token(); + } while (cur_cmd == spacer_cmd); + if (cur_tok == other_token + '=') { + get_token(); + if (cur_cmd == spacer_cmd) + get_token(); + } + } else { + p = null; + tex_error("invalid number for \\letcharcode",NULL); + } + } + if (cur_cmd >= call_cmd) + add_token_ref(cur_chr); + define(p, cur_cmd, cur_chr); + break; + case shorthand_def_cmd: + /* We temporarily define |p| to be |relax|, so that an occurrence of |p| + while scanning the definition will simply stop the scanning instead of + producing an ``undefined control sequence'' error or expanding the + previous meaning. This allows, for instance, `\.{\\chardef\\foo=123\\foo}'. + */ + n = cur_chr; + get_r_token(); + p = cur_cs; + define(p, relax_cmd, too_big_char); + scan_optional_equals(); + switch (n) { + case char_def_code: + scan_char_num(); + define(p, char_given_cmd, cur_val); + break; + case math_char_def_code: + mval = scan_mathchar(tex_mathcode); + if (math_umathcode_meaning_par == 1) { + cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; + define(p, xmath_given_cmd, cur_val); + } else { + cur_val = (mval.class_value * 16 + mval.family_value) * 256 + mval.character_value; + define(p, math_given_cmd, cur_val); + } + break; + case xmath_char_def_code: + mval = scan_mathchar(umath_mathcode); + cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; + define(p, xmath_given_cmd, cur_val); + break; + case umath_char_def_code: + mval = scan_mathchar(umathnum_mathcode); + cur_val = (mval.class_value + (8 * mval.family_value)) * (65536 * 32) + mval.character_value; + define(p, xmath_given_cmd, cur_val); + break; + default: + scan_register_num(); + switch (n) { + case count_def_code: + define(p, assign_int_cmd, count_base + cur_val); + break; + case attribute_def_code: + define(p, assign_attr_cmd, attribute_base + cur_val); + break; + case dimen_def_code: + define(p, assign_dimen_cmd, scaled_base + cur_val); + break; + case skip_def_code: + define(p, assign_glue_cmd, skip_base + cur_val); + break; + case mu_skip_def_code: + define(p, assign_mu_glue_cmd, mu_skip_base + cur_val); + break; + case toks_def_code: + define(p, assign_toks_cmd, toks_base + cur_val); + break; + default: + confusion("shorthand_def"); + break; + } + break; + } + break; + case read_to_cs_cmd: + j = cur_chr; + scan_int(); + n = cur_val; + if (!scan_keyword("to")) { + print_err("Missing `to' inserted"); + help2("You should have said `\\read to \\cs'.", + "I'm going to look for the \\cs now."); + error(); + } + get_r_token(); + p = cur_cs; + read_toks(n, p, j); + define(p, call_cmd, cur_val); + break; + case toks_register_cmd: + case assign_toks_cmd: + /* The token-list parameters, \.{\\output} and \.{\\everypar}, etc., receive + their values in the following way. (For safety's sake, we place an + enclosing pair of braces around an \.{\\output} list.) */ + q = cur_cs; + if (cur_cmd == toks_register_cmd) { + scan_register_num(); + p = toks_base + cur_val; + } else { + p = cur_chr; /* |p=every_par_loc| or |output_routine_loc| or \dots */ + } + scan_optional_equals(); + /* Get the next non-blank non-relax non-call token */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + + if (cur_cmd != left_brace_cmd) { + /* If the right-hand side is a token parameter + or token register, finish the assignment and |goto done| */ + if (cur_cmd == toks_register_cmd) { + scan_register_num(); + cur_cmd = assign_toks_cmd; + cur_chr = toks_base + cur_val; + } + if (cur_cmd == assign_toks_cmd) { + q = equiv(cur_chr); + if (q == null) { + define(p, undefined_cs_cmd, null); + } else { + add_token_ref(q); + define(p, call_cmd, q); + } + goto DONE; + } + } + back_input(); + cur_cs = q; + q = scan_toks(false, false); + if (token_link(def_ref) == null) { /* empty list: revert to the default */ + define(p, undefined_cs_cmd, null); + free_avail(def_ref); + } else { + if (p == output_routine_loc) { /* enclose in curlies */ + p = get_avail(); + set_token_link(q, p); + p = output_routine_loc; + q = token_link(q); + set_token_info(q, right_brace_token + '}'); + q = get_avail(); + set_token_info(q, left_brace_token + '{'); + set_token_link(q, token_link(def_ref)); + set_token_link(def_ref, q); + } + define(p, call_cmd, def_ref); + } + break; + case assign_int_cmd: + /* Similar routines are used to assign values to the numeric parameters. */ + p = cur_chr; + scan_optional_equals(); + scan_int(); + assign_internal_value(a, p, cur_val); + break; + case assign_attr_cmd: + p = cur_chr; + scan_optional_equals(); + scan_int(); + if ((p - attribute_base) > max_used_attr) + max_used_attr = (p - attribute_base); + attr_list_cache = cache_disabled; + word_define(p, cur_val); + break; + case assign_dir_cmd: + /* DIR: Assign direction codes */ + scan_direction(); + switch (cur_chr) { + case int_base + page_direction_code: + eq_word_define(int_base + page_direction_code, cur_val); + break; + case int_base + body_direction_code: + eq_word_define(int_base + body_direction_code, cur_val); + break; + case int_base + par_direction_code: + eq_word_define(int_base + par_direction_code, cur_val); + break; + case int_base + math_direction_code: + eq_word_define(int_base + math_direction_code, cur_val); + break; + case int_base + text_direction_code: + case int_base + line_direction_code: + /* + pre version 0.97 this was a commented section because various tests hint that this + is unnecessary and sometimes even produces weird results, like: + + (\hbox{\textdir TRT ABC\textdir TLT DEF})) + + becomes + + (DEFCBA) + + in the output when we use + + tail_append(new_dir(text_direction_par) + + but when we append the reverse of the current it goes better + + */ + check_glue = (cur_chr == (int_base + line_direction_code)); + if (check_glue) { + cur_chr = int_base + text_direction_code ; + } + if (abs(mode) == hmode) { + if (no_local_dirs_par > 0) { + /* tail is non zero but we test anyway */ + if (check_glue && (tail != null && type(tail) == glue_node)) { + halfword prev = alink(tail); + halfword dirn = new_dir(text_direction_par - dir_swap); + couple_nodes(prev,dirn); + couple_nodes(dirn,tail); + } else { + tail_append(new_dir(text_direction_par - dir_swap)); + } + } else { + /* what is the use of nolocaldirs .. maybe we should get rid of it */ + } + update_text_dir_ptr(cur_val); + tail_append(new_dir(cur_val)); + dir_level(tail) = cur_level; + } else { + update_text_dir_ptr(cur_val); + } + /* original: + + // if ((no_local_dirs_par > 0) && (abs(mode) == hmode)) { + // // tail_append(new_dir(text_direction_par) // kind of wrong + // tail_append(new_dir(text_direction_par - dir_swap)); // better + // } + + update_text_dir_ptr(cur_val); + if (abs(mode) == hmode) { + tail_append(new_dir(cur_val)); + dir_level(tail) = cur_level; + } + */ + eq_word_define(int_base + text_direction_code, cur_val); + eq_word_define(int_base + no_local_dirs_code, no_local_dirs_par + 1); + break; + } + break; + case assign_dimen_cmd: + p = cur_chr; + scan_optional_equals(); + scan_normal_dimen(); + assign_internal_value(a, p, cur_val); + break; + case assign_glue_cmd: + case assign_mu_glue_cmd: + p = cur_chr; + n = cur_cmd; + scan_optional_equals(); + if (n == assign_mu_glue_cmd) + scan_glue(mu_val_level); + else + scan_glue(glue_val_level); + define(p, glue_ref_cmd, cur_val); + break; + case def_char_code_cmd: + case def_del_code_cmd: + /* Let |n| be the largest legal code value, based on |cur_chr| */ + if (cur_chr == cat_code_base) + n = max_char_code; + else if (cur_chr == sf_code_base) + n = 077777; + else + n = biggest_char; + + p = cur_chr; + if (cur_chr == math_code_base) { + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + scan_extdef_math_code(cur_val1, tex_mathcode); + } else if (cur_chr == lc_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(lc_code_base); + define_lc_code(p, cur_val); + } else if (cur_chr == uc_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(uc_code_base); + define_uc_code(p, cur_val); + } else if (cur_chr == sf_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(sf_code_base); + define_sf_code(p, cur_val); + } else if (cur_chr == cat_code_base) { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + check_def_code(cat_code_base); + define_cat_code(p, cur_val); + } else if (cur_chr == del_code_base) { + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + scan_extdef_del_code(cur_val1, tex_mathcode); + } + break; + case extdef_math_code_cmd: + case extdef_del_code_cmd: + if (is_global(a)) + cur_val1 = level_one; + else + cur_val1 = cur_level; + if (cur_chr == math_code_base) + scan_extdef_math_code(cur_val1, umath_mathcode); + else if (cur_chr == math_code_base + 1) + scan_extdef_math_code(cur_val1, umathnum_mathcode); + else if (cur_chr == del_code_base) + scan_extdef_del_code(cur_val1, umath_mathcode); + else if (cur_chr == del_code_base + 1) + scan_extdef_del_code(cur_val1, umathnum_mathcode); + break; + case def_family_cmd: + p = cur_chr; + scan_math_family_int(); + cur_val1 = cur_val; + scan_optional_equals(); + scan_font_ident(); + define_fam_fnt(cur_val1, p, cur_val); + break; + case set_math_param_cmd: + p = cur_chr; + get_token(); + if (cur_cmd != math_style_cmd) { + print_err("Missing math style, treated as \\displaystyle"); + help1 + ("A style should have been here; I inserted `\\displaystyle'."); + cur_val1 = display_style; + back_error(); + } else { + cur_val1 = cur_chr; + } + scan_optional_equals(); + if (p < math_param_first_mu_glue) { + if (p == math_param_radical_degree_raise) + scan_int(); + else + scan_dimen(false, false, false); + } else { + scan_glue(mu_val_level); + if (cur_val == thin_mu_skip_par) + cur_val = thin_mu_skip_code; + else if (cur_val == med_mu_skip_par) + cur_val = med_mu_skip_code; + else if (cur_val == thick_mu_skip_par) + cur_val = thick_mu_skip_code; + } + define_math_param(p, cur_val1, cur_val); + break; + case register_cmd: + case advance_cmd: + case multiply_cmd: + case divide_cmd: + do_register_command(a); + break; + case set_box_cmd: + /* The processing of boxes is somewhat different, because we may need + to scan and create an entire box before we actually change the value + of the old one. */ + scan_register_num(); + if (is_global(a)) + n = global_box_flag + cur_val; + else + n = box_flag + cur_val; + scan_optional_equals(); + if (set_box_allowed) { + scan_box(n); + } else { + print_err("Improper \\setbox"); + help2("Sorry, \\setbox is not allowed after \\halign in a display,", + "or between \\accent and an accented character."); + error(); + } + break; + case set_aux_cmd: + /* The |space_factor| or |prev_depth| settings are changed when a |set_aux| + command is sensed. Similarly, |prev_graf| is changed in the presence of + |set_prev_graf|, and |dead_cycles| or |insert_penalties| in the presence of + |set_page_int|. These definitions are always global. */ + alter_aux(); + break; + case set_prev_graf_cmd: + alter_prev_graf(); + break; + case set_page_dimen_cmd: + alter_page_so_far(); + break; + case set_page_int_cmd: + alter_integer(); + break; + case set_box_dimen_cmd: + /* When some dimension of a box register is changed, the change isn't exactly + global; but \TeX\ does not look at the \.{\\global} switch. */ + alter_box_dimen(); + break; + case set_tex_shape_cmd: + q = cur_chr; + scan_optional_equals(); + scan_int(); + n = cur_val; + if (n <= 0) { + p = null; + } else { + p = new_node(shape_node, 2 * (n + 1) + 1); + vinfo(p + 1) = n; + for (j = 1; j <= n; j++) { + scan_normal_dimen(); + varmem[p + 2 * j].cint = cur_val; /* indentation */ + scan_normal_dimen(); + varmem[p + 2 * j + 1].cint = cur_val; /* width */ + } + } + define(q, shape_ref_cmd, p); + break; + case set_etex_shape_cmd: + q = cur_chr; + scan_optional_equals(); + scan_int(); + n = cur_val; + if (n <= 0) { + p = null; + } else { + n = (cur_val / 2) + 1; + p = new_node(shape_node, 2 * n + 1 + 1); + vinfo(p + 1) = n; + n = cur_val; + varmem[p + 2].cint = n; /* number of penalties */ + for (j = p + 3; j <= p + n + 2; j++) { + scan_int(); + varmem[j].cint = cur_val; /* penalty values */ + } + if (!odd(n)) + varmem[p + n + 3].cint = 0; /* unused */ + } + define(q, shape_ref_cmd, p); + break; + case hyph_data_cmd: + /* All of \TeX's parameters are kept in |eqtb| except the font information, + the interaction mode, and the hyphenation tables; these are strictly global. + */ + switch (cur_chr) { + case 0: + new_hyph_exceptions(); + break; + case 1: + new_patterns(); + break; + case 2: + new_pre_hyphen_char(); + break; + case 3: + new_post_hyphen_char(); + break; + case 4: + new_pre_exhyphen_char(); + break; + case 5: + new_post_exhyphen_char(); + break; + case 6: + new_hyphenation_min(); + break; + case 7: + new_hj_code(); + break; + } + break; + case assign_font_dimen_cmd: + set_font_dimen(); + break; + case assign_font_int_cmd: + n = cur_chr; + scan_font_ident(); + f = cur_val; + if (n == no_lig_code) { + set_no_ligatures(f); + } else if (n < lp_code_base) { + scan_optional_equals(); + scan_int(); + if (n == 0) + set_hyphen_char(f, cur_val); + else + set_skew_char(f, cur_val); + } else { + scan_char_num(); + p = cur_val; + scan_optional_equals(); + scan_int(); + switch (n) { + case lp_code_base: + set_lp_code(f, p, cur_val); + break; + case rp_code_base: + set_rp_code(f, p, cur_val); + break; + case ef_code_base: + set_ef_code(f, p, cur_val); + break; + case tag_code: + set_tag_code(f, p, cur_val); + break; + } + } + break; + case def_font_cmd: + /* Here is where the information for a new font gets loaded. */ + tex_def_font((small_number) a); + break; + case letterspace_font_cmd: + new_letterspaced_font((small_number) a); + break; + case copy_font_cmd: + make_font_copy((small_number) a); + break; + case set_font_id_cmd: + scan_int(); + if (is_valid_font(cur_val)) + zset_cur_font(cur_val); + break ; + case set_interaction_cmd: + new_interaction(); + break; + default: + confusion("prefix"); + break; + } /* end of Assignments cases */ + DONE: + /* Insert a token saved by \.{\\afterassignment}, if any */ + if (after_token != 0) { + cur_tok = after_token; + back_input(); + after_token = 0; + } +} + +@ @c +void fixup_directions(void) +{ + int temp_no_whatsits = no_local_whatsits_par; + int temp_no_dirs = no_local_dirs_par; + int temporary_dir = text_direction_par; + if (dir_level(text_dir_ptr) == cur_level) { + /* DIR: Remove from |text_dir_ptr| */ + halfword text_dir_tmp = vlink(text_dir_ptr); + flush_node(text_dir_ptr); + text_dir_ptr = text_dir_tmp; + } + unsave(); + if (abs(mode) == hmode) { + if (temp_no_dirs != 0) { + /* DIR: Add local dir node */ + tail_append(new_dir(text_direction_par)); + dir_dir(tail) = temporary_dir - dir_swap; + } + if (temp_no_whatsits != 0) { + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node(hmode_par_par_code)); + } + } +} + +@ When a control sequence is to be defined, by \.{\\def} or \.{\\let} or +something similar, the |get_r_token| routine will substitute a special +control sequence for a token that is not redefinable. + +@c +void get_r_token(void) +{ + RESTART: + do { + get_token(); + } while (cur_tok == space_token); + if ((cur_cs == 0) || (cur_cs > eqtb_top) || + ((cur_cs > frozen_control_sequence) && (cur_cs <= eqtb_size))) { + print_err("Missing control sequence inserted"); + help5("Please don't say `\\def cs{...}', say `\\def\\cs{...}'.", + "I've inserted an inaccessible control sequence so that your", + "definition will be completed without mixing me up too badly.", + "You can recover graciously from this error, if you're", + "careful; see exercise 27.2 in The TeXbook."); + if (cur_cs == 0) + back_input(); + cur_tok = cs_token_flag + frozen_protection; + ins_error(); + goto RESTART; + } +} + +@ @c +void assign_internal_value(int a, halfword p, int val) +{ + halfword n; + if ((p >= int_base) && (p < attribute_base)) { + switch ((p - int_base)) { + case cat_code_table_code: + if (valid_catcode_table(val)) { + if (val != cat_code_table_par) + word_define(p, val); + } else { + print_err("Invalid \\catcode table"); + help2 + ("You can only switch to a \\catcode table that is initialized", + "using \\savecatcodetable or \\initcatcodetable, or to table 0"); + error(); + } + break; + case output_box_code: + if ((val > 65535) | (val < 0)) { + print_err("Invalid \\outputbox"); + help1 + ("The value for \\outputbox has to be between 0 and 65535."); + error(); + } else { + word_define(p, val); + } + break; + case new_line_char_code: + if (val > 127) { + print_err("Invalid \\newlinechar"); + help2 + ("The value for \\newlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored."); + error(); + } else { + word_define(p, val); + } + break; + case end_line_char_code: + if (val > 127) { + print_err("Invalid \\endlinechar"); + help2 + ("The value for \\endlinechar has to be no higher than 127.", + "Your invalid assignment will be ignored."); + error(); + } else { + word_define(p, val); + } + break; + case language_code: + if (val < 0) { + word_define(int_base + cur_lang_code, -1); + word_define(p, -1); + } else if (val > 16383) { + print_err("Invalid \\language"); + help2 + ("The absolute value for \\language has to be no higher than 16383.", + "Your invalid assignment will be ignored."); + error(); + } else { + word_define(int_base + cur_lang_code, val); + word_define(p, val); + } + break; + default: + word_define(p, val); + break; + } + /* If we are defining subparagraph penalty levels while we are + in hmode, then we put out a whatsit immediately, otherwise + we leave it alone. This mechanism might not be sufficiently + powerful, and some other algorithm, searching down the stack, + might be necessary. Good first step. */ + if ((abs(mode) == hmode) && + ((p == (int_base + local_inter_line_penalty_code)) || + (p == (int_base + local_broken_penalty_code)))) { + /* LOCAL: Add local paragraph node */ + tail_append(make_local_par_node(penalty_par_code)); + eq_word_define(int_base + no_local_whatsits_code, no_local_whatsits_par + 1); + } + } else if ((p >= dimen_base) && (p <= eqtb_size)) { + if (p == (dimen_base + page_left_offset_code)) { + n = val - one_true_inch; + word_define(dimen_base + h_offset_code, n); + } else if (p == (dimen_base + h_offset_code)) { + n = val + one_true_inch; + word_define(dimen_base + page_left_offset_code, n); + } else if (p == (dimen_base + page_top_offset_code)) { + n = val - one_true_inch; + word_define(dimen_base + v_offset_code, n); + } else if (p == (dimen_base + v_offset_code)) { + n = val + one_true_inch; + word_define(dimen_base + page_top_offset_code, n); + } + word_define(p, val); + } else if ((p >= local_base) && (p < toks_base)) { /* internal locals */ + define(p, call_cmd, val); + } else { + confusion("assign internal value"); + } +} + +@ We use the fact that |register= assign_int_cmd) && (cur_cmd <= assign_mu_glue_cmd)) { + l = cur_chr; + p = cur_cmd - assign_int_cmd; + goto FOUND; + } + if (cur_cmd != register_cmd) { + print_err("You can't use `"); + print_cmd_chr((quarterword) cur_cmd, cur_chr); + tprint("' after "); + print_cmd_chr((quarterword) q, 0); + help1("I'm forgetting what you said and not changing anything."); + error(); + return; + } + } + p = cur_chr; + scan_register_num(); + if (p == int_val_level) + l = cur_val + count_base; + else if (p == attr_val_level) + l = cur_val + attribute_base; + else if (p == dimen_val_level) + l = cur_val + scaled_base; + else if (p == glue_val_level) + l = cur_val + skip_base; + else if (p == mu_val_level) + l = cur_val + mu_skip_base; + FOUND: + if (q == register_cmd) { + scan_optional_equals(); + } else if (scan_keyword("by")) { + /* optional `\.{by}' */ + } + arith_error = false; + if (q < multiply_cmd) { + /* Compute result of |register| or |advance|, put it in |cur_val| */ + if (p < glue_val_level) { + if ((p == int_val_level) || (p == attr_val_level)) + scan_int(); + else + scan_normal_dimen(); + if (q == advance_cmd) + cur_val = cur_val + eqtb[l].cint; + } else { + /* we can probably save a copy */ + scan_glue(p); + if (q == advance_cmd) { + /* Compute the sum of two glue specs */ + halfword r = equiv(l); + q = new_spec(cur_val); + flush_node(cur_val); + width(q) = width(q) + width(r); + if (stretch(q) == 0) { + stretch_order(q) = normal; + } + if (stretch_order(q) == stretch_order(r)) { + stretch(q) = stretch(q) + stretch(r); + } else if ((stretch_order(q) < stretch_order(r)) && (stretch(r) != 0)) { + stretch(q) = stretch(r); + stretch_order(q) = stretch_order(r); + } + if (shrink(q) == 0) { + shrink_order(q) = normal; + } + if (shrink_order(q) == shrink_order(r)) { + shrink(q) = shrink(q) + shrink(r); + } else if ((shrink_order(q) < shrink_order(r)) && (shrink(r) != 0)) { + shrink(q) = shrink(r); + shrink_order(q) = shrink_order(r); + } + cur_val = q; + } + } + } else { + /* Compute result of |multiply| or |divide|, put it in |cur_val| */ + scan_int(); + if (p < glue_val_level) { + if (q == multiply_cmd) { + if ((p == int_val_level) || (p == attr_val_level)) { + cur_val = mult_integers(eqtb[l].cint, cur_val); + } else { + cur_val = nx_plus_y(eqtb[l].cint, cur_val, 0); + } + } else { + cur_val = x_over_n(eqtb[l].cint, cur_val); + } + } else { + halfword s = equiv(l); + halfword r = new_spec(s); + if (q == multiply_cmd) { + width(r) = nx_plus_y(width(s), cur_val, 0); + stretch(r) = nx_plus_y(stretch(s), cur_val, 0); + shrink(r) = nx_plus_y(shrink(s), cur_val, 0); + } else { + width(r) = x_over_n(width(s), cur_val); + stretch(r) = x_over_n(stretch(s), cur_val); + shrink(r) = x_over_n(shrink(s), cur_val); + } + cur_val = r; + } + } + if (arith_error) { + print_err("Arithmetic overflow"); + help2("I can't carry out that multiplication or division,", + "since the result is out of range."); + if (p >= glue_val_level) + flush_node(cur_val); + error(); + return; + } + if (p < glue_val_level) { + if (p == attr_val_level) { + if ((l - attribute_base) > max_used_attr) + max_used_attr = (l - attribute_base); + attr_list_cache = cache_disabled; + } + if ((p == int_val_level) || (p == dimen_val_level)) + assign_internal_value(a, l, cur_val); + else + word_define(l, cur_val); + } else { + define(l, glue_ref_cmd, cur_val); + } +} + +@ @c +void alter_aux(void) +{ + halfword c; /* |hmode| or |vmode| */ + if (cur_chr != abs(mode)) { + report_illegal_case(); + } else { + c = cur_chr; + scan_optional_equals(); + if (c == vmode) { + scan_normal_dimen(); + prev_depth_par = cur_val; + } else { + scan_int(); + if ((cur_val <= 0) || (cur_val > 32767)) { + print_err("Bad space factor"); + help1("I allow only values in the range 1..32767 here."); + int_error(cur_val); + } else { + space_factor_par = cur_val; + } + } + } +} + +@ @c +void alter_prev_graf(void) +{ + int p; /* index into |nest| */ + p = nest_ptr; + while (abs(nest[p].mode_field) != vmode) + decr(p); + scan_optional_equals(); + scan_int(); + if (cur_val < 0) { + print_err("Bad \\prevgraf"); + help1("I allow only nonnegative values here."); + int_error(cur_val); + } else { + nest[p].pg_field = cur_val; + } +} + +@ @c +void alter_page_so_far(void) +{ + int c; /* index into |page_so_far| */ + c = cur_chr; + scan_optional_equals(); + scan_normal_dimen(); + page_so_far[c] = cur_val; +} + +@ @c +void alter_integer(void) +{ + int c; /* 0 for \.{\\deadcycles}, 1 for \.{\\insertpenalties}, etc. */ + c = cur_chr; + scan_optional_equals(); + scan_int(); + if (c == 0) { + dead_cycles = cur_val; + } else if (c == 2) { + if ((cur_val < batch_mode) || (cur_val > error_stop_mode)) { + print_err("Bad interaction mode"); + help2("Modes are 0=batch, 1=nonstop, 2=scroll, and", + "3=errorstop. Proceed, and I'll ignore this case."); + int_error(cur_val); + } else { + cur_chr = cur_val; + new_interaction(); + } + } else { + insert_penalties = cur_val; + } +} + +@ @c +void alter_box_dimen(void) +{ + int c; /* |width_offset| or |height_offset| or |depth_offset| */ + int b; /* box number */ + c = cur_chr; + scan_register_num(); + b = cur_val; + scan_optional_equals(); + scan_normal_dimen(); + if (box(b) != null) + varmem[box(b) + c].cint = cur_val; +} + +@ @c +void new_interaction(void) +{ + print_ln(); + interaction = cur_chr; + if (interaction == batch_mode) + kpse_make_tex_discard_errors = 1; + else + kpse_make_tex_discard_errors = 0; + fixup_selector(log_opened_global); +} + +@ The \.{\\afterassignment} command puts a token into the global +variable |after_token|. This global variable is examined just after +every assignment has been performed. + +@c +halfword after_token; /* zero, or a saved token */ + +@ Here is a procedure that might be called `Get the next non-blank non-relax +non-call non-assignment token'. + +@c +void do_assignments(void) +{ + while (true) { + /* Get the next non-blank non-relax... */ + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + if (cur_cmd <= max_non_prefixed_command) + return; + set_box_allowed = false; + prefixed_command(); + set_box_allowed = true; + } +} + +@ @c +void open_or_close_in(void) +{ + int c; /* 1 for \.{\\openin}, 0 for \.{\\closein} */ + int n; /* stream number */ + char *fn; + c = cur_chr; + scan_four_bit_int(); + n = cur_val; + if (read_open[n] != closed) { + lua_a_close_in(read_file[n], (n + 1)); + read_open[n] = closed; + } + if (c != 0) { + scan_optional_equals(); + do { + get_x_token(); + } while ((cur_cmd == spacer_cmd) || (cur_cmd == relax_cmd)); + back_input(); + if (cur_cmd != left_brace_cmd) { + scan_file_name(); /* set |cur_name| to desired file name */ + if (cur_ext == get_nullstr()) + cur_ext = maketexstring(".tex"); + } else { + scan_file_name_toks(); + } + fn = pack_file_name(cur_name, cur_area, cur_ext); + if (lua_a_open_in(&(read_file[n]), fn, (n + 1))) { + read_open[n] = just_open; + } + } +} + +@ @c +boolean long_help_seen; /* has the long \.{\\errmessage} help been used? */ + +void issue_message(void) +{ + int old_setting; /* holds |selector| setting */ + int c; /* identifies \.{\\message} and \.{\\errmessage} */ + str_number s; /* the message */ + c = cur_chr; + (void) scan_toks(false, true); + old_setting = selector; + selector = new_string; + token_show(def_ref); + selector = old_setting; + flush_list(def_ref); + str_room(1); + s = make_string(); + if (c == 0) { + /* Print string |s| on the terminal */ + if (term_offset + (int) str_length(s) > max_print_line - 2) + print_ln(); + else if ((term_offset > 0) || (file_offset > 0)) + print_char(' '); + print(s); + update_terminal(); + + } else { + /* Print string |s| as an error message */ + /* If \.{\\errmessage} occurs often in |scroll_mode|, without user-defined + \.{\\errhelp}, we don't want to give a long help message each time. So we + give a verbose explanation only once. */ + print_err(""); + print(s); + if (err_help_par != null) { + use_err_help = true; + } else if (long_help_seen) { + help1("(That was another \\errmessage.)"); + } else { + if (interaction < error_stop_mode) + long_help_seen = true; + help4("This error message was generated by an \\errmessage", + "command, so I can't give any explicit help.", + "Pretend that you're Hercule Poirot: Examine all clues,", + "and deduce the truth by order and method."); + } + error(); + use_err_help = false; + + } + flush_str(s); +} + +@ The |error| routine calls on |give_err_help| if help is requested from +the |err_help| parameter. + +@c +void give_err_help(void) +{ + token_show(err_help_par); +} + +@ The \.{\\uppercase} and \.{\\lowercase} commands are implemented by +building a token list and then changing the cases of the letters in it. + +@c +void shift_case(void) +{ + halfword b; /* |lc_code_base| or |uc_code_base| */ + halfword p; /* runs through the token list */ + halfword t; /* token */ + halfword c; /* character code */ + halfword i; /* inbetween */ + b = cur_chr; + p = scan_toks(false, false); + p = token_link(def_ref); + while (p != null) { + /* Change the case of the token in |p|, if a change is appropriate */ + /* + When the case of a |chr_code| changes, we don't change the |cmd|. + We also change active characters. + */ + t = token_info(p); + if (t < cs_token_flag) { + c = t % STRING_OFFSET; + if (b == uc_code_base) + i = get_uc_code(c); + else + i = get_lc_code(c); + if (i != 0) + set_token_info(p, t - c + i); + } else if (is_active_cs(cs_text(t - cs_token_flag))) { + c = active_cs_value(cs_text(t - cs_token_flag)); + if (b == uc_code_base) + i = get_uc_code(c); + else + i = get_lc_code(c); + if (i != 0) + set_token_info(p, active_to_cs(i, true) + cs_token_flag); + } + p = token_link(p); + } + back_list(token_link(def_ref)); + free_avail(def_ref); /* omit reference count */ +} + +@ We come finally to the last pieces missing from |main_control|, namely the +`\.{\\show}' commands that are useful when debugging. + +@c +void show_whatever(void) +{ + halfword p; /* tail of a token list to show */ + int t; /* type of conditional being shown */ + int m; /* upper bound on |fi_or_else| codes */ + int l; /* line where that conditional began */ + int n; /* level of \.{\\if...\\fi} nesting */ + switch (cur_chr) { + case show_lists: + begin_diagnostic(); + show_activities(); + break; + case show_box_code: + /* Show the current contents of a box */ + scan_register_num(); + begin_diagnostic(); + tprint_nl("> \\box"); + print_int(cur_val); + print_char('='); + if (box(cur_val) == null) + tprint("void"); + else + show_box(box(cur_val)); + break; + case show_code: + /* Show the current meaning of a token, then |goto common_ending| */ + get_token(); + if (interaction == error_stop_mode) + wake_up_terminal(); + tprint_nl("> "); + if (cur_cs != 0) { + sprint_cs(cur_cs); + print_char('='); + } + print_meaning(); + goto COMMON_ENDING; + break; + /* Cases for |show_whatever| */ + case show_groups: + begin_diagnostic(); + show_save_groups(); + break; + case show_ifs: + begin_diagnostic(); + tprint_nl(""); + print_ln(); + if (cond_ptr == null) { + tprint_nl("### "); + tprint("no active conditionals"); + } else { + p = cond_ptr; + n = 0; + do { + incr(n); + p = vlink(p); + } while (p != null); + p = cond_ptr; + t = cur_if; + l = if_line; + m = if_limit; + do { + tprint_nl("### level "); + print_int(n); + tprint(": "); + print_cmd_chr(if_test_cmd, t); + if (m == fi_code) + tprint_esc("else"); + print_if_line(l); + decr(n); + t = if_limit_subtype(p); + l = if_line_field(p); + m = if_limit_type(p); + p = vlink(p); + } while (p != null); + } + break; + default: + /* Show the current value of some parameter or register, + then |goto common_ending| */ + p = the_toks(); + if (interaction == error_stop_mode) + wake_up_terminal(); + tprint_nl("> "); + token_show(temp_token_head); + flush_list(token_link(temp_token_head)); + goto COMMON_ENDING; + break; + } + /* Complete a potentially long \.{\\show} command */ + end_diagnostic(true); + print_err("OK"); + if (selector == term_and_log) { + if (tracing_online_par <= 0) { + selector = term_only; + tprint(" (see the transcript file)"); + selector = term_and_log; + } + } + COMMON_ENDING: + if (interaction < error_stop_mode) { + help0(); + decr(error_count); + } else if (tracing_online_par > 0) { + help3("This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists)."); + } else { + help5("This isn't an error message; I'm just \\showing something.", + "Type `I\\show...' to show more (e.g., \\show\\cs,", + "\\showthe\\count10, \\showbox255, \\showlists).", + "And type `I\\tracingonline=1\\show...' to show boxes and", + "lists on your terminal as well as in the transcript file."); + } + error(); +} + +@ @c +void initialize(void) +{ /* this procedure gets things started properly */ + int k; /* index into |mem|, |eqtb|, etc. */ + /* Initialize whatever \TeX\ might access */ + /* Set initial values of key variables */ + initialize_errors(); + initialize_arithmetic(); + max_used_attr = -1; + attr_list_cache = cache_disabled; + initialize_nesting(); + + /* Start a new current page */ + page_contents = empty; + page_tail = page_head; +#if 0 + vlink(page_head) = null; +#endif + last_glue = max_halfword; + last_penalty = 0; + last_kern = 0; + last_node_type = -1; + page_depth = 0; + page_max_depth = 0; + + initialize_equivalents(); + no_new_control_sequence = true; /* new identifiers are usually forbidden */ + init_primitives(); + + mag_set = 0; + initialize_marks(); + initialize_read(); + + static_pdf = init_pdf_struct(static_pdf); /* should be init_backend() */ + + format_ident = 0; + format_name = get_nullstr(); + initialize_directions(); + initialize_write_files(); + seconds_and_micros(epochseconds, microseconds); + initialize_start_time(static_pdf); + + edit_name_start = 0; + stop_at_space = true; + + if (ini_version) { + /* Initialize table entries (done by \.{INITEX} only) */ + + init_node_mem(500); + initialize_tokens(); + /* Initialize the special list heads and constant nodes */ + initialize_alignments(); + initialize_buildpage(); + + initialize_active(); + + set_eq_type(undefined_control_sequence, undefined_cs_cmd); + set_equiv(undefined_control_sequence, null); + set_eq_level(undefined_control_sequence, level_zero); + for (k = null_cs; k <= (eqtb_top - 1); k++) + eqtb[k] = eqtb[undefined_control_sequence]; + set_equiv(glue_base, zero_glue); + set_eq_level(glue_base, level_one); + set_eq_type(glue_base, glue_ref_cmd); + for (k = glue_base + 1; k <= local_base - 1; k++) { + eqtb[k] = eqtb[glue_base]; + } + par_shape_par_ptr = null; + set_eq_type(par_shape_loc, shape_ref_cmd); + set_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 + biggest_reg; k++) + eqtb[k] = eqtb[undefined_control_sequence]; + box(0) = null; + set_eq_type(box_base, box_ref_cmd); + set_eq_level(box_base, level_one); + for (k = box_base + 1; k <= (box_base + biggest_reg); k++) + eqtb[k] = eqtb[box_base]; + cur_font_par = null_font; + set_eq_type(cur_font_loc, data_cmd); + set_eq_level(cur_font_loc, level_one); + set_equiv(cat_code_base, 0); + set_eq_type(cat_code_base, data_cmd); + set_eq_level(cat_code_base, level_one); + eqtb[internal_math_param_base] = eqtb[cat_code_base]; + eqtb[lc_code_base] = eqtb[cat_code_base]; + eqtb[uc_code_base] = eqtb[cat_code_base]; + eqtb[sf_code_base] = eqtb[cat_code_base]; + eqtb[math_code_base] = eqtb[cat_code_base]; + cat_code_table_par = 0; + initialize_math_codes(); + initialize_text_codes(); + initex_cat_codes(0); + for (k = '0'; k <= '9'; k++) + set_math_code(k, math_use_current_family_code, 0, k, level_one); + for (k = 'A'; k <= 'Z'; k++) { + set_math_code(k, math_use_current_family_code, 1, k, level_one); + set_math_code((k + 32), math_use_current_family_code, 1, (k + 32), level_one); + set_lc_code(k, k + 32, level_one); + set_lc_code(k + 32, k + 32, level_one); + set_uc_code(k, k, level_one); + set_uc_code(k + 32, k, level_one); + set_sf_code(k, 999, level_one); + } + for (k = int_base; k <= attribute_base - 1; k++) + eqtb[k].cint = 0; + for (k = attribute_base; k <= del_code_base - 1; k++) + eqtb[k].cint = UNUSED_ATTRIBUTE; + mag_par = 1000; + tolerance_par = 10000; + hang_after_par = 1; + max_dead_cycles_par = 25; + math_pre_display_gap_factor_par = 2000; + escape_char_par = '\\'; + end_line_char_par = carriage_return; + set_del_code('.', 0, 0, 0, 0, level_one); /* this null delimiter is used in error recovery */ + ex_hyphen_char_par = '-'; + output_box_par = 255; + for (k = dimen_base; k <= eqtb_size; k++) + eqtb[k].cint = 0; + page_left_offset_par = one_inch; + page_top_offset_par = one_inch; + page_right_offset_par = one_inch; + page_bottom_offset_par = one_inch; + ini_init_primitives(); + hash_used = frozen_control_sequence; /* nothing is used */ + hash_high = 0; + cs_count = 0; + set_eq_type(frozen_dont_expand, dont_expand_cmd); + cs_text(frozen_dont_expand) = maketexstring("notexpanded:"); + set_eq_type(frozen_primitive, ignore_spaces_cmd); + set_equiv(frozen_primitive, 1); + set_eq_level(frozen_primitive, level_one); + cs_text(frozen_primitive) = maketexstring("primitive"); + create_null_font(); + font_bytes = 0; + px_dimen_par = one_bp; + math_eqno_gap_step_par = 1000 ; + cs_text(frozen_protection) = maketexstring("inaccessible"); + format_ident = maketexstring(" (INITEX)"); + cs_text(end_write) = maketexstring("endwrite"); + set_eq_level(end_write, level_one); + set_eq_type(end_write, outer_call_cmd); + set_equiv(end_write, null); + + } + synctexoffset = int_base + synctex_code; + +} diff --git a/Build/source/texk/web2c/luatexdir/tex/mlist.w b/Build/source/texk/web2c/luatexdir/tex/mlist.w index 32fd32d4c0e..7a002c2843d 100644 --- a/Build/source/texk/web2c/luatexdir/tex/mlist.w +++ b/Build/source/texk/web2c/luatexdir/tex/mlist.w @@ -2645,11 +2645,13 @@ static scaled make_op(pointer q, int cur_style) small_fam(y) = math_fam(nucleus(q)); small_char(y) = math_character(nucleus(q)); x = do_delimiter(q, y, text_size, ok_size, false, cur_style, true, NULL, &delta); - if (do_new_math(cur_f)) { - /* we never added italic correction */ - } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { - /* remove italic correction */ - width(x) -= delta; + if (delta != 0) { + if (do_new_math(cur_f)) { + /* we never added italic correction */ + } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { + /* remove italic correction */ + width(x) -= delta; + } } } else { ok_size = height_plus_depth(cur_f, cur_c) + 1; @@ -2667,7 +2669,7 @@ static scaled make_op(pointer q, int cur_style) /* we never added italic correction */ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { /* remove italic correction */ - width(x) = width(x) - delta; + width(x) -= delta; } } axis_shift = true; @@ -2681,7 +2683,7 @@ static scaled make_op(pointer q, int cur_style) /* we never added italic correction */ } else if ((subscr(q) != null) && (subtype(q) != op_noad_type_limits)) { /* remove italic correction */ - width(x) = width(x) - delta; + width(x) -= delta; } } axis_shift = true; @@ -2710,7 +2712,7 @@ static scaled make_op(pointer q, int cur_style) /* make_scripts(q, p, 0, cur_style, delta, -delta); */ - int mode = nolimits_mode_par; /* wins */ + int mode = math_nolimits_mode_par; /* wins */ /* for easy configuration ... fonts are somewhat inconsistent and the values for italic correction run from 30 to 60% of the width @@ -3296,7 +3298,7 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled */ x = clean_box(subscr(q), sub_style(cur_style), cur_style); width(x) = width(x) + space_after_script(cur_style); - switch (scripts_mode_par) { + switch (math_scripts_mode_par) { case 1: shift_down = sub_shift_down(cur_style) ; break; @@ -3346,7 +3348,7 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled */ x = clean_box(supscr(q), sup_style(cur_style), cur_style); width(x) = width(x) + space_after_script(cur_style); - switch (scripts_mode_par) { + switch (math_scripts_mode_par) { case 1: shift_up = sup_shift_up(cur_style); break; @@ -3401,7 +3403,7 @@ static void make_scripts(pointer q, pointer p, scaled it, int cur_style, scaled */ y = clean_box(subscr(q), sub_style(cur_style), cur_style); width(y) = width(y) + space_after_script(cur_style); - switch (scripts_mode_par) { + switch (math_scripts_mode_par) { case 1: shift_down = sub_shift_down(cur_style) ; break; @@ -3866,6 +3868,7 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) int t_subtype; /* the effective |subtype| of noad |q| during the second pass */ pointer p = null; pointer z = null; + halfword nxt ; int pen; /* a penalty to be inserted */ scaled max_hl = 0; /* maximum height of the list translated so far */ scaled max_d = 0; /* maximum depth of the list translated so far */ @@ -3886,6 +3889,7 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) */ RESWITCH: delta = 0; + nxt = vlink(q); switch (type(q)) { case simple_noad: switch (subtype(q)) { @@ -4074,8 +4078,42 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) */ p = check_nucleus_complexity(q, &delta, cur_style); - if ((subscr(q) == null) && (supscr(q) == null)) { + /* + Adding italic correction here is kind of fuzzy because some + characters already have that built in. However, we also add + it in the scripts so if it's optional here it also should + be there. + */ + if (nxt && (math_italics_mode_par > 0) && (delta != 0)) { + if (type(nxt) == simple_noad) { + switch (subtype(nxt)) { + case ord_noad_type: + case bin_noad_type: + case rel_noad_type: + case open_noad_type: + case close_noad_type: + case punct_noad_type: + case inner_noad_type: + delta = 0; + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + case under_noad_type: + case over_noad_type: + case vcenter_noad_type: + break; + default: + break; + } + } + if (delta != 0) { + pointer d = new_kern(delta); + reset_attributes(d, node_attr(q)); + couple_nodes(p,d); + } + } assign_new_hlist(q, p); } else { /* top, bottom */ @@ -4219,7 +4257,7 @@ void mlist_to_hlist(pointer mlist, boolean penalties, int cur_style) r_type = type(vlink(q)); r_subtype = subtype(vlink(q)); if (r_type != penalty_node && (r_type != simple_noad || r_subtype != rel_noad_type)) { - z = new_penalty(pen); + z = new_penalty(pen,noad_penalty); reset_attributes(z, node_attr(q)); couple_nodes(p,z); p = z; diff --git a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w index fe11d76d0b1..dd85cf01b3e 100644 --- a/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w +++ b/Build/source/texk/web2c/luatexdir/tex/postlinebreak.w @@ -505,7 +505,7 @@ void ext_post_line_break(int paragraph_dir, } } if (pen != 0) { - r = new_penalty(pen); + r = new_penalty(pen,linebreak_penalty); couple_nodes(cur_list.tail_field, r); cur_list.tail_field = r; } diff --git a/Build/source/texk/web2c/luatexdir/tex/texmath.w b/Build/source/texk/web2c/luatexdir/tex/texmath.w index 769fbe8dd35..eeeb75bd792 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texmath.w +++ b/Build/source/texk/web2c/luatexdir/tex/texmath.w @@ -1,2569 +1,2569 @@ -% texmath.w -% -% Copyright 2008-2010 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c -#include "ptexlib.h" - -@ @c -#define mode mode_par -#define tail tail_par -#define head head_par -#define dir_save dirs_par - -/* - - \mathdisplayskipmode - - tex normally always inserts before and only after when larger than zero - - 0 = normal tex - 1 = always - 2 = non-zero - 3 = ignore - -*/ - -@ TODO: not sure if this is the right order -@c -#define back_error(A,B) do { \ - OK_to_interrupt=false; \ - back_input(); \ - OK_to_interrupt=true; \ - tex_error(A,B); \ - } while (0) - -@ @c -int scan_math(pointer, int); -int scan_math_style(pointer, int); -pointer fin_mlist(pointer); - -@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an -{\sl mlist}, which is essentially a tree structure representing that -formula. An mlist is a linear sequence of items, but we can regard it as -a tree structure because mlists can appear within mlists. For example, many -of the entries can be subscripted or superscripted, and such ``scripts'' -are mlists in their own right. - -An entire formula is parsed into such a tree before any of the actual -typesetting is done, because the current style of type is usually not -known until the formula has been fully scanned. For example, when the -formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell -that `\.{a+b}' will be in script size until `\.{\\over}' has appeared. - -During the scanning process, each element of the mlist being built is -classified as a relation, a binary operator, an open parenthesis, etc., -or as a construct like `\.{\\sqrt}' that must be built up. This classification -appears in the mlist data structure. - -After a formula has been fully scanned, the mlist is converted to an hlist -so that it can be incorporated into the surrounding text. This conversion is -controlled by a recursive procedure that decides all of the appropriate -styles by a ``top-down'' process starting at the outermost level and working -in towards the subformulas. The formula is ultimately pasted together using -combinations of horizontal and vertical boxes, with glue and penalty nodes -inserted as necessary. - -An mlist is represented internally as a linked list consisting chiefly -of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat -similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are -allowed to appear in mlists together with the noads; \TeX\ tells the difference -by means of the |type| field, since a noad's |type| is always greater than -that of a node. An mlist does not contain character nodes, hlist nodes, vlist -nodes, math nodes or unset nodes; in particular, each mlist item appears in the -variable-size part of |mem|, so the |type| field is always present. - -Each noad is five or more words long. The first word contains the -|type| and |subtype| and |link| fields that are already so familiar to -us; the second contains the attribute list pointer, and the third, -fourth an fifth words are called the noad's |nucleus|, |subscr|, and -|supscr| fields. (This use of a combined attribute list is temporary. -Eventually, each of fields need their own list) - -Consider, for example, the simple formula `\.{\$x\^2\$}', which would be -parsed into an mlist containing a single element called an |ord_noad|. -The |nucleus| of this noad is a representation of `\.x', the |subscr| is -empty, and the |supscr| is a representation of `\.2'. - -The |nucleus|, |subscr|, and |supscr| fields are further broken into -subfields. If |p| points to a noad, and if |q| is one of its principal -fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the -corresponding attribute of noad |p| is not present). Otherwise, there are -several possibilities for the subfields, depending on the |type| of |q|. - -\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of -the sixteen font families, and |character(q)| is the number of a character -within a font of that family, as in a character node. - -\yskip\hang|type(q)=math_text_char_node| is similar, but the character is -unsubscripted and unsuperscripted and it is followed immediately by another -character from the same font. (This |type| setting appears only -briefly during the processing; it is used to suppress unwanted italic -corrections.) - -\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box -node (either an |hlist_node| or a |vlist_node|) that should be used as the -value of the field. The |shift_amount| in the subsidiary box node is the -amount by which that box will be shifted downward. - -\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to -an mlist; the mlist must be converted to an hlist in order to obtain -the value of this field. - -\yskip\noindent In the latter case, we might have |math_list(q)=null|. This -is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}' -and `\.{\$P\$}' produce different results (the former will not have the -``italic correction'' added to the width of |P|, but the ``script skip'' -will be added). - -@c -static void unsave_math(void) -{ - unsave(); - decr(save_ptr); - flush_node_list(text_dir_ptr); - assert(saved_type(0) == saved_textdir); - text_dir_ptr = saved_value(0); -} - -@ Sometimes it is necessary to destroy an mlist. The following -subroutine empties the current list, assuming that |abs(mode)=mmode|. - -@c -void flush_math(void) -{ - flush_node_list(vlink(head)); - flush_node_list(incompleat_noad_par); - vlink(head) = null; - tail = head; - incompleat_noad_par = null; -} - -@ Before we can do anything in math mode, we need fonts. - -@c -#define MATHFONTSTACK 8 -#define MATHFONTDEFAULT 0 /* == nullfont */ - -static sa_tree math_fam_head = NULL; - -@ @c -int fam_fnt(int fam_id, int size_id) -{ - int n = fam_id + (256 * size_id); - return (int) get_sa_item(math_fam_head, n).int_value; -} - -void def_fam_fnt(int fam_id, int size_id, int f, int lvl) -{ - int n = fam_id + (256 * size_id); - sa_tree_item sa_value = { 0 }; - sa_value.int_value = f; - set_sa_item(math_fam_head, n, sa_value, lvl); - fixup_math_parameters(fam_id, size_id, f, lvl); - if (tracing_assigns_par > 1) { - begin_diagnostic(); - tprint("{assigning"); - print_char(' '); - print_cmd_chr(def_family_cmd, size_id); - print_int(fam_id); - print_char('='); - print_font_identifier(fam_fnt(fam_id, size_id)); - print_char('}'); - end_diagnostic(false); - } -} - -@ @c -static void unsave_math_fam_data(int gl) -{ - sa_stack_item st; - if (math_fam_head->stack == NULL) - return; - while (math_fam_head->stack_ptr > 0 && - abs(math_fam_head->stack[math_fam_head->stack_ptr].level) - >= (int) gl) { - st = math_fam_head->stack[math_fam_head->stack_ptr]; - if (st.level > 0) { - rawset_sa_item(math_fam_head, st.code, st.value); - /* now do a trace message, if requested */ - if (tracing_restores_par > 1) { - int size_id = st.code / 256; - int fam_id = st.code % 256; - begin_diagnostic(); - tprint("{restoring"); - print_char(' '); - print_cmd_chr(def_family_cmd, size_id); - print_int(fam_id); - print_char('='); - print_font_identifier(fam_fnt(fam_id, size_id)); - print_char('}'); - end_diagnostic(false); - } - } - (math_fam_head->stack_ptr)--; - } -} - -@ and parameters - -@c -#define MATHPARAMSTACK 8 -#define MATHPARAMDEFAULT undefined_math_parameter - -static sa_tree math_param_head = NULL; - -@ @c -void def_math_param(int param_id, int style_id, scaled value, int lvl) -{ - int n = param_id + (256 * style_id); - sa_tree_item sa_value = { 0 }; - sa_value.int_value = (int) value; - set_sa_item(math_param_head, n, sa_value, lvl); - if (tracing_assigns_par > 1) { - begin_diagnostic(); - tprint("{assigning"); - print_char(' '); - print_cmd_chr(set_math_param_cmd, param_id); - print_cmd_chr(math_style_cmd, style_id); - print_char('='); - print_int(value); - print_char('}'); - end_diagnostic(false); - } -} - -scaled get_math_param(int param_id, int style_id) -{ - int n = param_id + (256 * style_id); - return (scaled) get_sa_item(math_param_head, n).int_value; -} - -@ @c -static void unsave_math_param_data(int gl) -{ - sa_stack_item st; - if (math_param_head->stack == NULL) - return; - while (math_param_head->stack_ptr > 0 && - abs(math_param_head->stack[math_param_head->stack_ptr].level) - >= (int) gl) { - st = math_param_head->stack[math_param_head->stack_ptr]; - if (st.level > 0) { - rawset_sa_item(math_param_head, st.code, st.value); - /* now do a trace message, if requested */ - if (tracing_restores_par > 1) { - int param_id = st.code % 256; - int style_id = st.code / 256; - begin_diagnostic(); - tprint("{restoring"); - print_char(' '); - print_cmd_chr(set_math_param_cmd, param_id); - print_cmd_chr(math_style_cmd, style_id); - print_char('='); - print_int(get_math_param(param_id, style_id)); - print_char('}'); - end_diagnostic(false); - } - } - (math_param_head->stack_ptr)--; - } -} - -@ saving and unsaving of both - -@c -void unsave_math_data(int gl) -{ - unsave_math_fam_data(gl); - unsave_math_param_data(gl); -} - -@ Dumping and undumping -@c -void dump_math_data(void) -{ - sa_tree_item sa_value = { 0 }; - if (math_fam_head == NULL) { - sa_value.int_value = MATHFONTDEFAULT; - math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); - } - dump_sa_tree(math_fam_head, "mathfonts"); - if (math_param_head == NULL) { - sa_value.int_value = MATHPARAMDEFAULT; - math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); - } - dump_sa_tree(math_param_head, "mathparameters"); -} - -void undump_math_data(void) -{ - math_fam_head = undump_sa_tree("mathfonts"); - math_param_head = undump_sa_tree("mathparameters"); -} - -@ @c -void initialize_math(void) -{ - sa_tree_item sa_value = { 0 }; - if (math_fam_head == NULL) { - sa_value.int_value = MATHFONTDEFAULT; - math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); - } - if (math_param_head == NULL) { - sa_value.int_value = MATHPARAMDEFAULT; - math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); - initialize_math_spacing(); - } - return; -} - -@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, -Clo, Pun, or Inn, for purposes of spacing and line breaking. An -|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, -|punct_noad|, or |inner_noad| is used to represent portions of the various -types. For example, an `\.=' sign in a formula leads to the creation of a -|rel_noad| whose |nucleus| field is a representation of an equals sign -(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel} -also results in a |rel_noad|. When a |rel_noad| is followed by an -|op_noad|, say, and possibly separated by one or more ordinary nodes (not -noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) -just after the formula that corresponds to the |rel_noad|, unless there -already was a penalty immediately following; and a ``thick space'' will be -inserted just before the formula that corresponds to the |op_noad|. - -A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually -has a |subtype=normal|. The only exception is that an |op_noad| might -have |subtype=limits| or |no_limits|, if the normal positioning of -limits has been overridden for this operator. - -A |radical_noad| also has a |left_delimiter| field, which usually -represents a square root sign. - -A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|. - -Delimiter fields have four subfields -called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields -represent variable-size delimiters by giving the ``small'' and ``large'' -starting characters, as explained in Chapter~17 of {\sl The \TeX book}. -@:TeXbook}{\sl The \TeX book@> - -A |fraction_noad| is actually quite different from all other noads. -It has |thickness|, |denominator|, and |numerator| fields instead of -|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value -that tells how thick to make a fraction rule; however, the special -value |default_code| is used to stand for the -|default_rule_thickness| of the current size. The |numerator| and -|denominator| point to mlists that define a fraction; we always have -$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The -|left_delimiter| and |right_delimiter| fields specify delimiters that will -be placed at the left and right of the fraction. In this way, a -|fraction_noad| is able to represent all of \TeX's operators \.{\\over}, -\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and - \.{\\abovewithdelims}. - -@ The |new_noad| function creates an |ord_noad| that is completely null - -@c -pointer new_noad(void) -{ - pointer p; - p = new_node(simple_noad, ord_noad_type); - /* all noad fields are zero after this */ - return p; -} - -@ @c -pointer new_sub_box(pointer curbox) -{ - pointer p, q; - p = new_noad(); - q = new_node(sub_box_node, 0); - nucleus(p) = q; - math_list(nucleus(p)) = curbox; - return p; -} - -@ A few more kinds of noads will complete the set: An |under_noad| has its -nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places -an accent over its nucleus; the accent character appears as -|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad| -centers its nucleus vertically with respect to the axis of the formula; -in such noads we always have |type(nucleus(p))=sub_box|. - -And finally, we have the |fence_noad| type, to implement -\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}. -The |nucleus| of such noads is -replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces -a |fence_noad| such that |delimiter(p)| holds the family and character -codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| -never appears in an mlist except as the first element, and a |fence_noad| -with subtype |right_noad_side| never appears in an mlist -except as the last element; furthermore, we either have both a |left_noad_side| -and a |right_noad_side|, or neither one is present. - -@ Math formulas can also contain instructions like \.{\\textstyle} that -override \TeX's normal style rules. A |style_node| is inserted into the -data structure to record such instructions; it is three words long, so it -is considered a node instead of a noad. The |subtype| is either |display_style| -or |text_style| or |script_style| or |script_script_style|. The -second and third words of a |style_node| are not used, but they are -present because a |choice_node| is converted to a |style_node|. - -\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles -|display_style|, \dots, |script_script_style|, and adds~1 to get the -``cramped'' versions of these styles. This gives a numerical order that -is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; -i.e., a smaller style has a larger numerical value. -@:TeXbook}{\sl The \TeX book@> - -@c -const char *math_style_names[] = { - "display", "crampeddisplay", - "text", "crampedtext", - "script", "crampedscript", - "scriptscript", "crampedscriptscript", - NULL -}; - -const char *math_param_names[] = { - "quad", "axis", "operatorsize", - "overbarkern", "overbarrule", "overbarvgap", - "underbarkern", "underbarrule", "underbarvgap", - "radicalkern", "radicalrule", "radicalvgap", - "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise", - "stackvgap", "stacknumup", "stackdenomdown", - "fractionrule", "fractionnumvgap", "fractionnumup", - "fractiondenomvgap", "fractiondenomdown", "fractiondelsize", - "limitabovevgap", "limitabovebgap", "limitabovekern", - "limitbelowvgap", "limitbelowbgap", "limitbelowkern", - "nolimitsubfactor", "nolimitsupfactor", /* bonus */ - "underdelimitervgap", "underdelimiterbgap", - "overdelimitervgap", "overdelimiterbgap", - "subshiftdrop", "supshiftdrop", "subshiftdown", - "subsupshiftdown", "subtopmax", "supshiftup", - "supbottommin", "supsubbottommax", "subsupvgap", - "spaceafterscript", "connectoroverlapmin", - "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing", - "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing", - "opordspacing", "opopspacing", "opbinspacing", "oprelspacing", - "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing", - "binordspacing", "binopspacing", "binbinspacing", "binrelspacing", - "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing", - "relordspacing", "relopspacing", "relbinspacing", "relrelspacing", - "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", - "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", - "openopenspacing", "openclosespacing", "openpunctspacing", - "openinnerspacing", - "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", - "closeopenspacing", "closeclosespacing", "closepunctspacing", - "closeinnerspacing", - "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", - "punctopenspacing", "punctclosespacing", "punctpunctspacing", - "punctinnerspacing", - "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", - "inneropenspacing", "innerclosespacing", "innerpunctspacing", - "innerinnerspacing", - NULL -}; - -@ @c -pointer new_style(small_number s) -{ /* create a style node */ - m_style = s; - return new_node(style_node, s); -} - -@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which -has special subfields |display_mlist|, |text_mlist|, |script_mlist|, -and |script_script_mlist| pointing to the mlists for each style. - -@c -static pointer new_choice(void) -{ /* create a choice node */ - return new_node(choice_node, 0); /* the |subtype| is not used */ -} - -@ Let's consider now the previously unwritten part of |show_node_list| -that displays the things that can only be present in mlists; this -program illustrates how to access the data structures just defined. - -In the context of the following program, |p| points to a node or noad that -should be displayed, and the current string contains the ``recursion history'' -that leads to this point. The recursion history consists of a dot for each -outer level in which |p| is subsidiary to some node, or in which |p| is -subsidiary to the |nucleus| field of some noad; the dot is replaced by -`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| -or |supscr| or |denominator| or |numerator| fields of noads. For example, -the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for -|x| in the (ridiculous) formula -`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. - -@c -void display_normal_noad(pointer p); /* forward */ -void display_fence_noad(pointer p); /* forward */ -void display_fraction_noad(pointer p); /* forward */ - -void show_math_node(pointer p) -{ - switch (type(p)) { - case style_node: - print_cmd_chr(math_style_cmd, subtype(p)); - break; - case choice_node: - tprint_esc("mathchoice"); - append_char('D'); - show_node_list(display_mlist(p)); - flush_char(); - append_char('T'); - show_node_list(text_mlist(p)); - flush_char(); - append_char('S'); - show_node_list(script_mlist(p)); - flush_char(); - append_char('s'); - show_node_list(script_script_mlist(p)); - flush_char(); - break; - case simple_noad: - case radical_noad: - case accent_noad: - display_normal_noad(p); - break; - case fence_noad: - display_fence_noad(p); - break; - case fraction_noad: - display_fraction_noad(p); - break; - default: - tprint("Unknown node type!"); - break; - } -} - -@ Here are some simple routines used in the display of noads. - -@c -static void print_fam_and_char(pointer p) -{ /* prints family and character */ - tprint_esc("fam"); - print_int(math_fam(p)); - print_char(' '); - print(math_character(p)); -} - -@ @c -static void print_delimiter(pointer p) -{ - int a; - if (delimiteroptionset(p)) { - tprint(" [ "); - if (delimiteraxis(p)) - tprint("axis "); - if (delimiternoaxis(p)) - tprint("noaxis "); - if (delimiterexact(p)) - tprint("exact "); - tprint("]"); - } - if (delimiterheight(p)) { - tprint("height="); - print_scaled(delimiterheight(p)); - tprint(" "); - } - if (delimiterdepth(p)) { - tprint("depth="); - print_scaled(delimiterdepth(p)); - tprint(" "); - } - if (delimiterclass(p)) { - tprint("class="); - print_int(delimiterclass(p)); - tprint(" "); - } - if (small_fam(p) < 0) { - print_int(-1); /* this should never happen */ - } else if (small_fam(p) < 16 && large_fam(p) < 16 && - small_char(p) < 256 && large_char(p) < 256) { - /* traditional tex style */ - a = small_fam(p) * 256 + small_char(p); - a = a * 0x1000 + large_fam(p) * 256 + large_char(p); - print_hex(a); - } else if ((large_fam(p) == 0 && large_char(p) == 0) || - small_char(p) > 65535 || large_char(p) > 65535) { - /* modern xetex/luatex style */ - print_hex(small_fam(p)); - print_hex(small_char(p)); - } -} - -@ The next subroutine will descend to another level of recursion when a -subsidiary mlist needs to be displayed. The parameter |c| indicates what -character is to become part of the recursion history. An empty mlist is -distinguished from a missing field, because these are not equivalent -(as explained above). -@^recursion@> - -@c -static void print_subsidiary_data(pointer p, ASCII_code c) -{ /* display a noad field */ - if ((int) cur_length >= depth_threshold) { - if (p != null) - tprint(" []"); - } else { - append_char(c); /* include |c| in the recursion history */ - if (p != null) { - switch (type(p)) { - case math_char_node: - print_ln(); - print_current_string(); - print_fam_and_char(p); - break; - case sub_box_node: - show_node_list(math_list(p)); - break; - case sub_mlist_node: - if (math_list(p) == null) { - print_ln(); - print_current_string(); - tprint("{}"); - } else { - show_node_list(math_list(p)); - } - break; - } - } - flush_char(); /* remove |c| from the recursion history */ - } -} - -@ @c -void display_normal_noad(pointer p) -{ - switch (type(p)) { - case simple_noad: - switch (subtype(p)) { - case ord_noad_type: - tprint_esc("mathord"); - break; - case op_noad_type_normal: - case op_noad_type_limits: - case op_noad_type_no_limits: - tprint_esc("mathop"); - if (subtype(p) == op_noad_type_limits) - tprint_esc("limits"); - else if (subtype(p) == op_noad_type_no_limits) - tprint_esc("nolimits"); - break; - case bin_noad_type: - tprint_esc("mathbin"); - break; - case rel_noad_type: - tprint_esc("mathrel"); - break; - case open_noad_type: - tprint_esc("mathopen"); - break; - case close_noad_type: - tprint_esc("mathclose"); - break; - case punct_noad_type: - tprint_esc("mathpunct"); - break; - case inner_noad_type: - tprint_esc("mathinner"); - break; - case over_noad_type: - tprint_esc("overline"); - break; - case under_noad_type: - tprint_esc("underline"); - break; - case vcenter_noad_type: - tprint_esc("vcenter"); - break; - default: - tprint(""); - break; - } - break; - case radical_noad: - if (subtype(p) == 6) - tprint_esc("Udelimiterover"); - else if (subtype(p) == 5) - tprint_esc("Udelimiterunder"); - else if (subtype(p) == 4) - tprint_esc("Uoverdelimiter"); - else if (subtype(p) == 3) - tprint_esc("Uunderdelimiter"); - else if (subtype(p) == 2) - tprint_esc("Uroot"); - else - tprint_esc("radical"); - print_delimiter(left_delimiter(p)); - if (degree(p) != null) { - print_subsidiary_data(degree(p), '/'); - } - if (radicalwidth(p)) { - tprint("width="); - print_scaled(radicalwidth(p)); - tprint(" "); - } - if (radicaloptionset(p)) { - tprint(" [ "); - if (radicalexact(p)) - tprint("exact "); - if (radicalleft(p)) - tprint("left "); - if (radicalmiddle(p)) - tprint("middle "); - if (radicalright(p)) - tprint("right "); - tprint("]"); - } - break; - case accent_noad: - if (top_accent_chr(p) != null) { - if (bot_accent_chr(p) != null) { - tprint_esc("Umathaccent both"); - } else { - tprint_esc("Umathaccent"); - } - } else if (bot_accent_chr(p) != null) { - tprint_esc("Umathaccent bottom"); - } else { - tprint_esc("Umathaccent overlay"); - } - if (accentfraction(p)) { - tprint(" fraction="); - print_int(accentfraction(p)); - tprint(" "); - } - switch (subtype(p)) { - case 0: - if (top_accent_chr(p) != null) { - if (bot_accent_chr(p) != null) { - print_fam_and_char(top_accent_chr(p)); - print_fam_and_char(bot_accent_chr(p)); - } else { - print_fam_and_char(top_accent_chr(p)); - } - } else if (bot_accent_chr(p) != null) { - print_fam_and_char(bot_accent_chr(p)); - } else { - print_fam_and_char(overlay_accent_chr(p)); - } - break; - case 1: - if (top_accent_chr(p) != null) { - tprint(" fixed "); - print_fam_and_char(top_accent_chr(p)); - if (bot_accent_chr(p) != null) { - print_fam_and_char(bot_accent_chr(p)); - } - } else { - confusion("display_accent_noad"); - } - break; - case 2: - if (bot_accent_chr(p) != null) { - if (top_accent_chr(p) != null) { - print_fam_and_char(top_accent_chr(p)); - } - tprint(" fixed "); - print_fam_and_char(bot_accent_chr(p)); - } else{ - confusion("display_accent_noad"); - } - break; - case 3: - if (top_accent_chr(p) != null && bot_accent_chr(p) != null) { - tprint(" fixed "); - print_fam_and_char(top_accent_chr(p)); - tprint(" fixed "); - print_fam_and_char(bot_accent_chr(p)); - } else { - confusion("display_accent_noad"); - } - break; - } - break; - } - print_subsidiary_data(nucleus(p), '.'); - print_subsidiary_data(supscr(p), '^'); - print_subsidiary_data(subscr(p), '_'); -} - -@ @c -void display_fence_noad(pointer p) -{ - if (subtype(p) == right_noad_side) - tprint_esc("right"); - else if (subtype(p) == left_noad_side) - tprint_esc("left"); - else - tprint_esc("middle"); - print_delimiter(delimiter(p)); -} - -@ @c -void display_fraction_noad(pointer p) -{ - tprint_esc("fraction, thickness "); - if (thickness(p) == default_code) - tprint("= default"); - else - print_scaled(thickness(p)); - if ((left_delimiter(p) != null) && - ((small_fam(left_delimiter(p)) != 0) || - (small_char(left_delimiter(p)) != 0) || - (large_fam(left_delimiter(p)) != 0) || - (large_char(left_delimiter(p)) != 0))) { - tprint(", left-delimiter "); - print_delimiter(left_delimiter(p)); - } - if ((right_delimiter(p) != null) && - ((small_fam(right_delimiter(p)) != 0) || - (small_char(right_delimiter(p)) != 0) || - (large_fam(right_delimiter(p)) != 0) || - (large_char(right_delimiter(p)) != 0))) { - tprint(", right-delimiter "); - print_delimiter(right_delimiter(p)); - } - print_subsidiary_data(numerator(p), '\\'); - print_subsidiary_data(denominator(p), '/'); -} - -@ The routines that \TeX\ uses to create mlists are similar to those we have -just seen for the generation of hlists and vlists. But it is necessary to -make ``noads'' as well as nodes, so the reader should review the -discussion of math mode data structures before trying to make sense out of -the following program. - -Here is a little routine that needs to be done whenever a subformula -is about to be processed. The parameter is a code like |math_group|. - -@c -static void new_save_level_math(group_code c) -{ - set_saved_record(0, saved_textdir, 0, text_dir_ptr); - text_dir_ptr = new_dir(math_direction_par); - incr(save_ptr); - new_save_level(c); - eq_word_define(int_base + body_direction_code, math_direction_par); - eq_word_define(int_base + par_direction_code, math_direction_par); - eq_word_define(int_base + text_direction_code, math_direction_par); -} - -@ @c -static void push_math(group_code c, int mstyle) -{ - if (math_direction_par != text_direction_par) - dir_math_save = true; - push_nest(); - mode = -mmode; - incompleat_noad_par = null; - m_style = mstyle; - new_save_level_math(c); -} - -@ @c -static void enter_ordinary_math(void) -{ - push_math(math_shift_group, text_style); - eq_word_define(int_base + cur_fam_code, -1); - if (every_math_par != null) - begin_token_list(every_math_par, every_math_text); -} - -@ @c -void enter_display_math(void); - -@ We get into math mode from horizontal mode when a `\.\$' (i.e., a -|math_shift| character) is scanned. We must check to see whether this -`\.\$' is immediately followed by another, in case display math mode is -called for. - -@c -void init_math(void) -{ - if (cur_cmd == math_shift_cmd) { - get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ - if ((cur_cmd == math_shift_cmd) && (mode > 0)) { - enter_display_math(); - } else { - back_input(); - enter_ordinary_math(); - } - } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) { - enter_display_math(); - } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) { - enter_ordinary_math(); - } else { - you_cant(); - } -} - -@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or -`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; -the value of |cur_chr| is placed onto |save_stack| for safe keeping. - -@ When \TeX\ is in display math mode, |cur_group=math_shift_group|, -so it is not necessary for the |start_eq_no| procedure to test for -this condition. - -@c -void start_eq_no(void) -{ - set_saved_record(0, saved_eqno, 0, cur_chr); - incr(save_ptr); - enter_ordinary_math(); -} - -@ Subformulas of math formulas cause a new level of math mode to be entered, -on the semantic nest as well as the save stack. These subformulas arise in -several ways: (1)~A left brace by itself indicates the beginning of a -subformula that will be put into a box, thereby freezing its glue and -preventing line breaks. (2)~A subscript or superscript is treated as a -subformula if it is not a single character; the same applies to -the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive -initiates a subformula that will be terminated by a matching \.{\\right}. -The group codes placed on |save_stack| in these three cases are -|math_group|, |math_group|, and |math_left_group|, respectively. - -Here is the code that handles case (1); the other cases are not quite as -trivial, so we shall consider them later. - -@c -void math_left_brace(void) -{ - pointer q; - tail_append(new_noad()); - q = new_node(math_char_node, 0); - nucleus(tail) = q; - back_input(); - (void) scan_math(nucleus(tail), m_style); -} - -@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are -opposite, then this function will return true. Discovering that fact -is somewhat odd because it needs traversal of the |save_stack|. -The occurance of displayed equations is weird enough that this is -probably still better than having yet another field in the |input_stack| -structures. - -None of this makes much sense if the inline direction of either one of -\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current -math machinery is ill suited anyway so I do not bother to test that. - -@c -static boolean math_and_text_reversed_p(void) -{ - int i = save_ptr - 1; - while (save_type(i) != level_boundary) - i--; - while (i < save_ptr) { - if (save_type(i) == restore_old_value && - save_value(i) == int_base + par_direction_code) { - if (textdir_opposite(math_direction_par, save_value(i - 1))) - return true; - } - i++; - } - return false; -} - -@ When we enter display math mode, we need to call |line_break| to -process the partial paragraph that has just been interrupted by the -display. Then we can set the proper values of |display_width| and -|display_indent| and |pre_display_size|. - -@c -void enter_display_math(void) -{ - scaled w; /* new or partial |pre_display_size| */ - scaled l; /* new |display_width| */ - scaled s; /* new |display_indent| */ - pointer p; - int n; /* scope of paragraph shape specification */ - if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ - (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ - type(tail) == local_par_node && vlink(tail) == null)) { - if (vlink(head) == tail) { - /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if - there is another display immediately following, we have to get rid - of that node */ - flush_node(tail); - } - pop_nest(); - w = -max_dimen; - } else { - line_break(true, math_shift_group); - w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par); - } - /* now we are in vertical mode, working on the list that will contain the display */ - /* A displayed equation is considered to be three lines long, so we - calculate the length and offset of line number |prev_graf+2|. */ - if (par_shape_par_ptr == null) { - if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) { - halfword used_hang_indent = swap_hang_indent(hang_indent_par); - l = hsize_par - abs(used_hang_indent); - if (used_hang_indent > 0) - s = used_hang_indent; - else - s = 0; - } else { - l = hsize_par; - s = 0; - } - } else { - n = vinfo(par_shape_par_ptr + 1); - if (prev_graf_par + 2 >= n) - p = par_shape_par_ptr + 2 * n + 1; - else - p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1; - s = varmem[(p - 1)].cint; - l = varmem[p].cint; - s = swap_parshape_indent(s,l); - } - - push_math(math_shift_group, display_style); - mode = mmode; - eq_word_define(int_base + cur_fam_code, -1); - eq_word_define(dimen_base + pre_display_size_code, w); - eq_word_define(dimen_base + display_width_code, l); - eq_word_define(dimen_base + display_indent_code, s); - eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0)); - if (every_display_par != null) - begin_token_list(every_display_par, every_display_text); - if (nest_ptr == 1) { - checked_page_filter(before_display); - build_page(); - } -} - -@ The next routine parses all variations of a delimiter code. The |extcode| - tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the - |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). - (the class is passed on for conversion to \.{\\mathchar}). - -@c -static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) -{ - const char *hlp[] = { - "I'm going to use 0 instead of that illegal code value.", - NULL - }; - delcodeval d; - int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; - if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ - scan_int(); - /* "MFCCFCC or "FCCFCC */ - if (doclass) { - mcls = (cur_val / 0x1000000); - cur_val = (cur_val & 0xFFFFFF); - } - if (cur_val > 0xFFFFFF) { - tex_error("Invalid delimiter code", hlp); - cur_val = 0; - } - msfam = (cur_val / 0x100000); - mschr = (cur_val % 0x100000) / 0x1000; - mlfam = (cur_val & 0xFFF) / 0x100; - mlchr = (cur_val % 0x100); - } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */ - /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ - if (doclass) { - scan_int(); - mcls = cur_val; - } - scan_int(); - msfam = cur_val; - scan_char_num(); - mschr = cur_val; - if (msfam < 0 || msfam > 255) { - tex_error("Invalid delimiter code", hlp); - msfam = 0; - mschr = 0; - } - mlfam = 0; - mlchr = 0; - } else if (extcode == umathnum_mathcode) { /* \.{\\Udelcodenum} */ - /* "FF<21bits> */ - /* the largest numeric value is $2^29-1$, but - the top of bit 21 can't be used as it contains invalid USV's - */ - if (doclass) { /* such a primitive doesn't exist */ - confusion("umathnum_mathcode"); - } - scan_int(); - msfam = (cur_val / 0x200000); - mschr = cur_val & 0x1FFFFF; - if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { - tex_error("Invalid delimiter code", hlp); - msfam = 0; - mschr = 0; - } - mlfam = 0; - mlchr = 0; - } else { - /* something's gone wrong */ - confusion("unknown_extcode"); - } - d.class_value = mcls; - d.small_family_value = msfam; - d.small_character_value = mschr; - d.large_family_value = mlfam; - d.large_character_value = mlchr; - return d; -} - -@ @c -void scan_extdef_del_code(int level, int extcode) -{ - delcodeval d; - int p; - scan_char_num(); - p = cur_val; - scan_optional_equals(); - d = do_scan_extdef_del_code(extcode, false); - set_del_code(p, d.small_family_value, d.small_character_value, - d.large_family_value, d.large_character_value, - (quarterword) (level)); -} - -@ @c -mathcodeval scan_mathchar(int extcode) -{ - char errstr[255] = { 0 }; - const char *hlp[] = { - "I'm going to use 0 instead of that illegal code value.", - NULL - }; - mathcodeval d; - int mcls = 0, mfam = 0, mchr = 0; - if (extcode == tex_mathcode) { /* \.{\\mathcode} */ - /* "TFCC */ - scan_int(); - if (cur_val > 0x8000) { - /* - tex_error("Invalid math code", hlp); - cur_val = 0; - */ - /* needed for latex: fallback to umathnum_mathcode */ - mfam = (cur_val / 0x200000) & 0x7FF; - mcls = mfam % 0x08; - mfam = mfam / 0x08; - mchr = cur_val & 0x1FFFFF; - if (mchr > 0x10FFFF) { - tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp); - mcls = 0; - mfam = 0; - mchr = 0; - } - } else { - if (cur_val < 0) { - snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val); - tex_error(errstr, hlp); - cur_val = 0; - } - mcls = (cur_val / 0x1000); - mfam = ((cur_val % 0x1000) / 0x100); - mchr = (cur_val % 0x100); - } - } else if (extcode == umath_mathcode) { - /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ - scan_int(); - mcls = cur_val; - scan_int(); - mfam = cur_val; - scan_char_num(); - mchr = cur_val; - if (mcls < 0 || mcls > 7 || mfam > 255) { - tex_error("Invalid math code", hlp); - mchr = 0; - mfam = 0; - mcls = 0; - } - } else if (extcode == umathnum_mathcode) { - /* "FFT<21bits> */ - /* the largest numeric value is $2^32-1$, but - the top of bit 21 can't be used as it contains invalid USV's - */ - /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */ - scan_int(); - mfam = (cur_val / 0x200000) & 0x7FF; - mcls = mfam % 0x08; - mfam = mfam / 0x08; - mchr = cur_val & 0x1FFFFF; - if (mchr > 0x10FFFF) { - tex_error("Invalid math code", hlp); - mcls = 0; - mfam = 0; - mchr = 0; - } - } else { - /* something's gone wrong */ - confusion("unknown_extcode"); - } - d.class_value = mcls; - d.family_value = mfam; - d.character_value = mchr; - return d; -} - -@ @c -void scan_extdef_math_code(int level, int extcode) -{ - mathcodeval d; - int p; - scan_char_num(); - p = cur_val; - scan_optional_equals(); - d = scan_mathchar(extcode); - set_math_code(p, d.class_value, - d.family_value, d.character_value, (quarterword) (level)); -} - -@ this reads in a delcode when actually a mathcode is needed -@c -mathcodeval scan_delimiter_as_mathchar(int extcode) -{ - delcodeval dval; - mathcodeval mval; - dval = do_scan_extdef_del_code(extcode, true); - mval.class_value = dval.class_value; - mval.family_value = dval.small_family_value; - mval.character_value = dval.small_character_value; - return mval; -} - -@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad -are broken down into subfields called |type| and either |math_list| or -|(math_fam,math_character)|. The job of |scan_math| is to figure out -what to place in one of these principal fields; it looks at the -subformula that comes next in the input, and places an encoding of -that subformula into a given word of |mem|. - -@c -#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) - -int scan_math_style(pointer p, int mstyle) -{ - get_next_nb_nr(); - back_input(); - scan_left_brace(); - set_saved_record(0, saved_math, 0, p); - incr(save_ptr); - push_math(math_group, mstyle); - return 1; -} - -int scan_math(pointer p, int mstyle) -{ - /* label restart,reswitch,exit; */ - mathcodeval mval = { 0, 0, 0 }; - assert(p != null); - RESTART: - get_next_nb_nr(); - RESWITCH: - switch (cur_cmd) { - case letter_cmd: - case other_char_cmd: - case char_given_cmd: - mval = get_math_code(cur_chr); - if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ - cur_cs = active_to_cs(cur_chr, true); - cur_cmd = eq_type(cur_cs); - cur_chr = equiv(cur_cs); - x_token(); - back_input(); - goto RESTART; - } - break; - case char_num_cmd: - scan_char_num(); - cur_chr = cur_val; - cur_cmd = char_given_cmd; - goto RESWITCH; - break; - case math_char_num_cmd: - if (cur_chr == 0) - mval = scan_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_mathchar(umath_mathcode); - else if (cur_chr == 2) - mval = scan_mathchar(umathnum_mathcode); - else - confusion("scan_math"); - break; - case math_given_cmd: - mval = mathchar_from_integer(cur_chr, tex_mathcode); - break; - case xmath_given_cmd: - mval = mathchar_from_integer(cur_chr, umath_mathcode); - break; - case delim_num_cmd: - if (cur_chr == 0) - mval = scan_delimiter_as_mathchar(tex_mathcode); - else if (cur_chr == 1) - mval = scan_delimiter_as_mathchar(umath_mathcode); - else - confusion("scan_math"); - break; - default: - /* The pointer |p| is placed on |save_stack| while a complex subformula - is being scanned. */ - back_input(); - scan_left_brace(); - set_saved_record(0, saved_math, 0, p); - incr(save_ptr); - push_math(math_group, mstyle); - return 1; - } - type(p) = math_char_node; - math_character(p) = mval.character_value; - if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(p) = cur_fam_par; - else - math_fam(p) = mval.family_value; - return 0; -} - -@ The |set_math_char| procedure creates a new noad appropriate to a given -math code, and appends it to the current mlist. However, if the math code -is sufficiently large, the |cur_chr| is treated as an active character and -nothing is appended. - -@c -void set_math_char(mathcodeval mval) -{ - pointer p; /* the new noad */ - if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ - cur_cs = active_to_cs(cur_chr, true); - cur_cmd = eq_type(cur_cs); - cur_chr = equiv(cur_cs); - x_token(); - back_input(); - } else { - pointer q; - p = new_noad(); - q = new_node(math_char_node, 0); - nucleus(p) = q; - math_character(nucleus(p)) = mval.character_value; - math_fam(nucleus(p)) = mval.family_value; - if (mval.class_value == math_use_current_family_code) { - if (cur_fam_par_in_range) - math_fam(nucleus(p)) = cur_fam_par; - subtype(p) = ord_noad_type; - } else { - switch (mval.class_value) { - /* *INDENT-OFF* */ - case 0: subtype(p) = ord_noad_type; break; - case 1: subtype(p) = op_noad_type_normal; break; - case 2: subtype(p) = bin_noad_type; break; - case 3: subtype(p) = rel_noad_type; break; - case 4: subtype(p) = open_noad_type; break; - case 5: subtype(p) = close_noad_type; break; - case 6: subtype(p) = punct_noad_type; break; - /* *INDENT-ON* */ - } - } - vlink(tail) = p; - tail = p; - } -} - -@ The |math_char_in_text| procedure creates a new node representing a math char -in text code, and appends it to the current list. However, if the math code -is sufficiently large, the |cur_chr| is treated as an active character and -nothing is appended. - -@c -void math_char_in_text(mathcodeval mval) -{ - pointer p; /* the new node */ - if (mval.class_value == 8) { - /* An active character that is an |outer_call| is allowed here */ - cur_cs = active_to_cs(cur_chr, true); - cur_cmd = eq_type(cur_cs); - cur_chr = equiv(cur_cs); - x_token(); - back_input(); - } else { - p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value); - vlink(tail) = p; - tail = p; - } -} - -@ @c -void math_math_comp(void) -{ - pointer q; - tail_append(new_noad()); - subtype(tail) = (quarterword) cur_chr; - q = new_node(math_char_node, 0); - nucleus(tail) = q; - if (cur_chr == over_noad_type) - (void) scan_math(nucleus(tail), cramped_style(m_style)); - else - (void) scan_math(nucleus(tail), m_style); -} - -@ @c -void math_limit_switch(void) -{ - const char *hlp[] = { - "I'm ignoring this misplaced \\limits or \\nolimits command.", - NULL - }; - if (head != tail) { - if (type(tail) == simple_noad && - (subtype(tail) == op_noad_type_normal || - subtype(tail) == op_noad_type_limits || - subtype(tail) == op_noad_type_no_limits)) { - subtype(tail) = (quarterword) cur_chr; - return; - } - } - tex_error("Limit controls must follow a math operator", hlp); -} - -@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. -The first parameter of this procedure is the |mem| address where the -delimiter is to be placed; the second tells if this delimiter follows -\.{\\radical} or not. - -@c -static void scan_delimiter(pointer p, int r) -{ - delcodeval dval = { 0, 0, 0, 0, 0 }; - if (r == tex_mathcode) { /* \.{\\radical} */ - dval = do_scan_extdef_del_code(tex_mathcode, true); - } else if (r == umath_mathcode) { /* \.{\\Uradical} */ - dval = do_scan_extdef_del_code(umath_mathcode, false); - } else if (r == no_mathcode) { - get_next_nb_nr(); - switch (cur_cmd) { - case letter_cmd: - case other_char_cmd: - dval = get_del_code(cur_chr); - break; - case delim_num_cmd: - if (cur_chr == 0) /* \.{\\delimiter} */ - dval = do_scan_extdef_del_code(tex_mathcode, true); - else if (cur_chr == 1) /* \.{\\Udelimiter} */ - dval = do_scan_extdef_del_code(umath_mathcode, true); - else - confusion("scan_delimiter1"); - break; - default: - dval.small_family_value = -1; - break; - } - } else { - confusion("scan_delimiter2"); - } - if (p == null) - return; - if (dval.small_family_value < 0) { - const char *hlp[] = { - "I was expecting to see something like `(' or `\\{' or", - "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", - "should probably delete the `{' by typing `1' now, so that", - "braces don't get unbalanced. Otherwise just proceed", - "Acceptable delimiters are characters whose \\delcode is", - "nonnegative, or you can use `\\delimiter '.", - NULL - }; - back_error("Missing delimiter (. inserted)", hlp); - small_fam(p) = 0; - small_char(p) = 0; - large_fam(p) = 0; - large_char(p) = 0; - } else { - small_fam(p) = dval.small_family_value; - small_char(p) = dval.small_character_value; - large_fam(p) = dval.large_family_value; - large_char(p) = dval.large_character_value; - } - return; -} - -@ @c -void math_radical(void) -{ - halfword q; - int chr_code = cur_chr; - halfword options = 0; - tail_append(new_node(radical_noad, chr_code)); - q = new_node(delim_node, 0); - left_delimiter(tail) = q; - while (1) { - if (scan_keyword("width")) { - scan_dimen(false,false,false); - radicalwidth(tail) = cur_val ; - } else if (scan_keyword("left")) { - options = options | noad_option_left ; - } else if (scan_keyword("middle")) { - options = options | noad_option_middle ; - } else if (scan_keyword("right")) { - options = options | noad_option_right ; - } else { - break; - } - } - radicaloptions(tail) = options; - if (chr_code == 0) /* \.{\\radical} */ - scan_delimiter(left_delimiter(tail), tex_mathcode); - else if (chr_code == 1) /* \.{\\Uradical} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 2) /* \.{\\Uroot} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 5) /* \.{\\Udelimiterunder} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 6) /* \.{\\Udelimiterover} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else if (chr_code == 7) /* \.{\\Uhextensible} */ - scan_delimiter(left_delimiter(tail), umath_mathcode); - else - confusion("math_radical"); - if (chr_code == 7) { - q = new_node(sub_box_node, 0); /* type will change */ - nucleus(tail) = q; - return; - } else if (chr_code == 2) { - /* the trick with the |vlink(q)| is used by |scan_math| - to decide whether it needs to go on */ - q = new_node(math_char_node, 0); - vlink(q) = tail; - degree(tail) = q; - if (!scan_math(degree(tail), sup_sup_style(m_style))) { - vlink(degree(tail)) = null; - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math(nucleus(tail), cramped_style(m_style)); - } - } else { - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math(nucleus(tail), cramped_style(m_style)); - } -} - -@ @c -void math_ac(void) -{ - halfword q; - mathcodeval t = { 0, 0, 0 }; - mathcodeval b = { 0, 0, 0 }; - mathcodeval o = { 0, 0, 0 }; - if (cur_cmd == accent_cmd) { - const char *hlp[] = { - "I'm changing \\accent to \\mathaccent here; wish me luck.", - "(Accents are not the same in formulas as they are in text.)", - NULL - }; - tex_error("Please use \\mathaccent for accents in math mode", hlp); - } - tail_append(new_node(accent_noad, 0)); - if (cur_chr == 0) { /* \.{\\mathaccent} */ - t = scan_mathchar(tex_mathcode); - } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ - if (scan_keyword("fixed")) { - /* top */ - subtype(tail) = 1; - t = scan_mathchar(umath_mathcode); - } else if (scan_keyword("both")) { - /* top bottom */ - if (scan_keyword("fixed")) { - subtype(tail) = 1; - } - t = scan_mathchar(umath_mathcode); - if (scan_keyword("fixed")) { - subtype(tail) += 2; - } - b = scan_mathchar(umath_mathcode); - } else if (scan_keyword("bottom")) { - /* bottom */ - if (scan_keyword("fixed")) { - subtype(tail) = 2; - } - b = scan_mathchar(umath_mathcode); - } else if (scan_keyword("top")) { - /* top */ - if (scan_keyword("fixed")) { - subtype(tail) = 1; - } - t = scan_mathchar(umath_mathcode); - } else if (scan_keyword("overlay")) { - /* overlay */ - if (scan_keyword("fixed")) { - subtype(tail) = 1; - } - o = scan_mathchar(umath_mathcode); - } else { - /* top */ - t = scan_mathchar(umath_mathcode); - } - if (scan_keyword("fraction")) { - scan_int(); - accentfraction(tail) = cur_val; - } - } else { - confusion("mathaccent"); - } - if (!(t.character_value == 0 && t.family_value == 0)) { - q = new_node(math_char_node, 0); - top_accent_chr(tail) = q; - math_character(top_accent_chr(tail)) = t.character_value; - if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(top_accent_chr(tail)) = cur_fam_par; - else - math_fam(top_accent_chr(tail)) = t.family_value; - } - if (!(b.character_value == 0 && b.family_value == 0)) { - q = new_node(math_char_node, 0); - bot_accent_chr(tail) = q; - math_character(bot_accent_chr(tail)) = b.character_value; - if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(bot_accent_chr(tail)) = cur_fam_par; - else - math_fam(bot_accent_chr(tail)) = b.family_value; - } - if (!(o.character_value == 0 && o.family_value == 0)) { - q = new_node(math_char_node, 0); - overlay_accent_chr(tail) = q; - math_character(overlay_accent_chr(tail)) = o.character_value; - if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range) - math_fam(overlay_accent_chr(tail)) = cur_fam_par; - else - math_fam(overlay_accent_chr(tail)) = o.family_value; - } - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math(nucleus(tail), cramped_style(m_style)); -} - -@ @c -pointer math_vcenter_group(pointer p) -{ - pointer q, r; - q = new_noad(); - subtype(q) = vcenter_noad_type; - r = new_node(sub_box_node, 0); - nucleus(q) = r; - math_list(nucleus(q)) = p; - return q; -} - -@ The routine that scans the four mlists of a \.{\\mathchoice} is very -much like the routine that builds discretionary nodes. - -@c -void append_choices(void) -{ - tail_append(new_choice()); - incr(save_ptr); - set_saved_record(-1, saved_choices, 0, 0); - push_math(math_choice_group, display_style); - scan_left_brace(); -} - -@ @c -void build_choices(void) -{ - pointer p; /* the current mlist */ - int prev_style; - prev_style = m_style; - unsave_math(); - p = fin_mlist(null); - assert(saved_type(-1) == saved_choices); - switch (saved_value(-1)) { - case 0: - display_mlist(tail) = p; - break; - case 1: - text_mlist(tail) = p; - break; - case 2: - script_mlist(tail) = p; - break; - case 3: - script_script_mlist(tail) = p; - decr(save_ptr); - return; - break; - } /* there are no other cases */ - set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); - push_math(math_choice_group, (prev_style + 2)); - scan_left_brace(); -} - -@ Subscripts and superscripts are attached to the previous nucleus by the -action procedure called |sub_sup|. - -@c -void sub_sup(void) -{ - pointer q; - if (tail == head || (!scripts_allowed(tail))) { - tail_append(new_noad()); - q = new_node(sub_mlist_node, 0); - nucleus(tail) = q; - } - if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ - if (supscr(tail) != null) { - const char *hlp[] = { - "I treat `x^1^2' essentially like `x^1{}^2'.", NULL - }; - tail_append(new_noad()); - q = new_node(sub_mlist_node, 0); - nucleus(tail) = q; - tex_error("Double superscript", hlp); - } - q = new_node(math_char_node, 0); - supscr(tail) = q; - (void) scan_math(supscr(tail), sup_style(m_style)); - } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) { - if (subscr(tail) != null) { - const char *hlp[] = { - "I treat `x_1_2' essentially like `x_1{}_2'.", NULL - }; - tail_append(new_noad()); - q = new_node(sub_mlist_node, 0); - nucleus(tail) = q; - tex_error("Double subscript", hlp); - } - q = new_node(math_char_node, 0); - subscr(tail) = q; - (void) scan_math(subscr(tail), sub_style(m_style)); - } -} - -@ An operation like `\.{\\over}' causes the current mlist to go into a -state of suspended animation: |incompleat_noad| points to a |fraction_noad| -that contains the mlist-so-far as its numerator, while the denominator -is yet to come. Finally when the mlist is finished, the denominator will -go into the incompleat fraction noad, and that noad will become the -whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' -delimiters. - -@c -void math_fraction(void) -{ - halfword c; /* the type of generalized fraction we are scanning */ - pointer q; - halfword options = 0; - halfword temp_value; - c = cur_chr; - if (incompleat_noad_par != null) { - const char *hlp[] = { - "I'm ignoring this fraction specification, since I don't", - "know whether a construction like `x \\over y \\over z'", - "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", - NULL - }; - if (c >= delimited_code) { - scan_delimiter(null, no_mathcode); - scan_delimiter(null, no_mathcode); - } - if ((c % delimited_code) == above_code) - scan_normal_dimen(); - tex_error("Ambiguous; you need another { and }", hlp); - } else { - incompleat_noad_par = new_node(fraction_noad, 0); - temp_value = new_node(sub_mlist_node, 0); - numerator(incompleat_noad_par) = temp_value; - math_list(numerator(incompleat_noad_par)) = vlink(head); - vlink(head) = null; - tail = head; - m_style = cramped_style(m_style); - - if ((c % delimited_code) == skewed_code) { - q = new_node(delim_node, 0); - middle_delimiter(incompleat_noad_par) = q; - scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode); - } - if (c >= delimited_code) { - q = new_node(delim_node, 0); - left_delimiter(incompleat_noad_par) = q; - q = new_node(delim_node, 0); - right_delimiter(incompleat_noad_par) = q; - scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode); - scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode); - } - switch (c % delimited_code) { - case above_code: - while (1) { - if (scan_keyword("exact")) { - options = options | noad_option_exact ; - } else { - break; - } - } - fractionoptions(incompleat_noad_par) = options; - scan_normal_dimen(); - thickness(incompleat_noad_par) = cur_val; - break; - case over_code: - thickness(incompleat_noad_par) = default_code; - break; - case atop_code: - thickness(incompleat_noad_par) = 0; - break; - case skewed_code: - while (1) { - if (scan_keyword("exact")) { - options = options | noad_option_exact ; - } else if (scan_keyword("noaxis")) { - options = options | noad_option_no_axis ; - } else { - break; - } - } - fractionoptions(incompleat_noad_par) = options; - thickness(incompleat_noad_par) = 0; - break; - } - } -} - -@ At the end of a math formula or subformula, the |fin_mlist| routine is -called upon to return a pointer to the newly completed mlist, and to -pop the nest back to the enclosing semantic level. The parameter to -|fin_mlist|, if not null, points to a |fence_noad| that ends the -current mlist; this |fence_noad| has not yet been appended. - -@c -pointer fin_mlist(pointer p) -{ - pointer q; /* the mlist to return */ - if (incompleat_noad_par != null) { - if (denominator(incompleat_noad_par) != null) { - type(denominator(incompleat_noad_par)) = sub_mlist_node; - } else { - q = new_node(sub_mlist_node, 0); - denominator(incompleat_noad_par) = q; - } - math_list(denominator(incompleat_noad_par)) = vlink(head); - if (p == null) { - q = incompleat_noad_par; - } else { - q = math_list(numerator(incompleat_noad_par)); - if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) - || (delim_par == null)) - confusion("right"); /* this can't happen */ - math_list(numerator(incompleat_noad_par)) = vlink(delim_par); - vlink(delim_par) = incompleat_noad_par; - vlink(incompleat_noad_par) = p; - } - } else { - vlink(tail) = p; - q = vlink(head); - } - pop_nest(); - return q; -} - -@ Now at last we're ready to see what happens when a right brace occurs -in a math formula. Two special cases are simplified here: Braces are effectively -removed when they surround a single Ord without sub/superscripts, or when they -surround an accent that is the nucleus of an Ord atom. - -@c -void close_math_group(pointer p) -{ - int old_style = m_style; - unsave_math(); - - decr(save_ptr); - assert(saved_type(0) == saved_math); - type(saved_value(0)) = sub_mlist_node; - p = fin_mlist(null); - math_list(saved_value(0)) = p; - if (p != null) { - if (vlink(p) == null) { - if (type(p) == simple_noad && subtype(p) == ord_noad_type) { - if (subscr(p) == null && supscr(p) == null) { - type(saved_value(0)) = type(nucleus(p)); - if (type(nucleus(p)) == math_char_node) { - math_fam(saved_value(0)) = math_fam(nucleus(p)); - math_character(saved_value(0)) = - math_character(nucleus(p)); - } else { - math_list(saved_value(0)) = math_list(nucleus(p)); - math_list(nucleus(p)) = null; - } - delete_attribute_ref(node_attr(saved_value(0))); - node_attr(saved_value(0)) = node_attr(nucleus(p)); - node_attr(nucleus(p)) = null; - flush_node(p); - } - } else if (type(p) == accent_noad) { - if (saved_value(0) == nucleus(tail)) { - if (type(tail) == simple_noad - && subtype(tail) == ord_noad_type) { - pointer q = head; - while (vlink(q) != tail) - q = vlink(q); - vlink(q) = p; - nucleus(tail) = null; - subscr(tail) = null; - supscr(tail) = null; - delete_attribute_ref(node_attr(p)); - node_attr(p) = node_attr(tail); - node_attr(tail) = null; - flush_node(tail); - tail = p; - } - } - } - } - } - if (vlink(saved_value(0)) > 0) { - pointer q; - q = new_node(math_char_node, 0); - nucleus(vlink(saved_value(0))) = q; - vlink(saved_value(0)) = null; - saved_value(0) = q; - (void) scan_math(saved_value(0), old_style); - /* restart */ - } -} - -@ We have dealt with all constructions of math mode except `\.{\\left}' and -`\.{\\right}', so the picture is completed by the following sections of -the program. The |middle| feature of eTeX allows one ore several \.{\\middle} -delimiters to appear between \.{\\left} and \.{\\right}. - -@c -void math_left_right(void) -{ - halfword t; /* |left_noad_side| .. |right_noad_side| */ - pointer p; /* new noad */ - pointer q; /* resulting mlist */ - pointer r; /* temporary */ - halfword ht = 0; - halfword dp = 0; - halfword options = 0; - halfword type = -1 ; - t = cur_chr; - - if (t > 10) { - /* we have \Uleft \Uright \Umiddle */ - t = t - 10; - while (1) { - if (scan_keyword("height")) { - scan_dimen(false,false,false); - ht = cur_val ; - } else if (scan_keyword("depth")) { - scan_dimen(false,false,false); - dp = cur_val ; - } else if (scan_keyword("axis")) { - options = options | noad_option_axis ; - } else if (scan_keyword("noaxis")) { - options = options | noad_option_no_axis ; - } else if (scan_keyword("exact")) { - options = options | noad_option_exact ; - } else if (scan_keyword("class")) { - scan_int(); - type = cur_val ; - } else { - break; - } - } - } - - if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) { - if (cur_group == math_shift_group) { - scan_delimiter(null, no_mathcode); - if (t == middle_noad_side) { - const char *hlp[] = { - "I'm ignoring a \\middle that had no matching \\left.", - NULL - }; - tex_error("Extra \\middle", hlp); - } else { - const char *hlp[] = { - "I'm ignoring a \\right that had no matching \\left.", - NULL - }; - tex_error("Extra \\right", hlp); - } - } else { - off_save(); - } - } else { - p = new_noad(); - type(p) = fence_noad; - subtype(p) = (quarterword) t; - r = new_node(delim_node, 0); - delimiter(p) = r; - - delimiterheight(p) = ht; - delimiterdepth(p) = dp; - delimiteroptions(p) = options; - delimiterclass(p) = type; - delimiteritalic(p) = 0; - - scan_delimiter(delimiter(p), no_mathcode); - - if (t == no_noad_side) { - tail_append(new_noad()); - subtype(tail) = inner_noad_type; - r = new_node(sub_mlist_node, 0); - nucleus(tail) = r; - math_list(nucleus(tail)) = p; - return ; - } - - if (t == left_noad_side) { - q = p; - } else { - q = fin_mlist(p); - unsave_math(); - } - if (t != right_noad_side) { - push_math(math_left_group, m_style); - vlink(head) = q; - tail = p; - delim_par = p; - } else { - tail_append(new_noad()); - subtype(tail) = inner_noad_type; - r = new_node(sub_mlist_node, 0); - nucleus(tail) = r; - math_list(nucleus(tail)) = q; - } - } -} - -@ \TeX\ gets to the following part of the program when -the first `\.\$' ending a display has been scanned. - -@c -static void check_second_math_shift(void) -{ - get_x_token(); - if (cur_cmd != math_shift_cmd) { - const char *hlp[] = { - "The `$' that I just saw supposedly matches a previous `$$'.", - "So I shall assume that you typed `$$' both times.", - NULL - }; - back_error("Display math should end with $$", hlp); - } -} - -static void check_display_math_end(void) -{ - if (cur_chr != cramped_display_style) { - const char *hlp[] = { - "I shall assume that you typed that.", - NULL - }; - tex_error("Display math should end with \\Ustopdisplaymath", hlp); - } -} - -static void check_inline_math_end(void) -{ - if (cur_chr != cramped_text_style) { - const char *hlp[] = { - "I shall assume that you typed that.", - NULL - }; - tex_error("Inline math should end with \\Ustopmath", hlp); - } -} - -@ @c -static void resume_after_display(void) -{ - if (cur_group != math_shift_group) - confusion("display"); - unsave_math(); - prev_graf_par = prev_graf_par + 3; - push_nest(); - mode = hmode; - space_factor_par = 1000; - /* this needs to be intercepted in the display math start ! */ - tail_append(make_local_par_node(penalty_par_code)); - get_x_token(); - if (cur_cmd != spacer_cmd) - back_input(); - if (nest_ptr == 1) { - normal_page_filter(after_display); - build_page(); - } -} - -@ The fussiest part of math mode processing occurs when a displayed formula is -being centered and placed with an optional equation number. - -At this time we are in vertical mode (or internal vertical mode). - - |p| points to the mlist for the formula. - |a| is either |null| or it points to a box containing the equation number. - |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). - -@c -#define inject_display_skip_before(g) \ - switch (display_skip_mode_par) { \ - case 0 : /* normal tex */ \ - tail_append(new_param_glue(g)); \ - break;\ - case 1 : /* always */ \ - tail_append(new_param_glue(g)); \ - break; \ - case 2 : /* non-zero */ \ - if (g != 0 && ! glue_is_zero(glue_par(g))) \ - tail_append(new_param_glue(g)); \ - break; \ - case 3: /* ignore */ \ - break; \ - } - -#define inject_display_skip_after(g) \ - switch (display_skip_mode_par) { \ - case 0 : /* normal tex */ \ - if (g != 0 && glue_is_positive(glue_par(g))) \ - tail_append(new_param_glue(g)); \ - break; \ - case 1 : /* always */ \ - tail_append(new_param_glue(g)); \ - break; \ - case 2 : /* non-zero */ \ - if (g != 0 && ! glue_is_zero(glue_par(g))) \ - tail_append(new_param_glue(g)); \ - break; \ - case 3: /* ignore */ \ - break; \ - } - -static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) -{ - pointer eq_box; /* box containing the equation */ - scaled eq_w; /* width of the equation */ - scaled line_w; /* width of the line */ - scaled eqno_w; /* width of equation number */ - scaled eqno_w2; /* width of equation number plus space to separate from equation */ - scaled line_s; /* move the line right this much */ - scaled d; /* displacement of equation in the line */ - small_number g1, g2; /* glue parameter codes for before and after */ - pointer r,s; /* kern nodes used to position the display */ - pointer t; /* tail of adjustment list */ - pointer pre_t; /* tail of pre-adjustment list */ - boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ - scaled eqno_width; - swap_dir = (pre_display_direction_par < 0 ? true : false ); - if (eqno_box != null && swap_dir) - l = !l; - adjust_tail = adjust_head; - pre_adjust_tail = pre_adjust_head; - eq_box = hpack(p, 0, additional, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - p = list_ptr(eq_box); - t = adjust_tail; - adjust_tail = null; - pre_t = pre_adjust_tail; - pre_adjust_tail = null; - eq_w = width(eq_box); - line_w = display_width_par; - line_s = display_indent_par; - if (eqno_box == null) { - eqno_w = 0; - eqno_width = 0; - eqno_w2 = 0; - } else { - eqno_w = width(eqno_box); - eqno_width = eqno_w; - eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000); - subtype(eqno_box) = equation_number_list; /* new */ - /* build_attribute_list(eqno_box); */ /* probably already set */ - } - if (eq_w + eqno_w2 > line_w) { - /* The user can force the equation number to go on a separate line - by causing its width to be zero. */ - if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) - || (total_shrink[sfi] != 0) - || (total_shrink[fil] != 0) - || (total_shrink[fill] != 0) - || (total_shrink[filll] != 0))) { - list_ptr(eq_box) = null; - flush_node(eq_box); - eq_box = hpack(p, line_w - eqno_w2, exactly, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - } else { - eqno_w = 0; - if (eq_w > line_w) { - list_ptr(eq_box) = null; - flush_node(eq_box); - eq_box = hpack(p, line_w, exactly, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - } - } - eq_w = width(eq_box); - } - /* We try first to center the display without regard to the existence of - the equation number. If that would make it too close (where ``too close'' - means that the space between display and equation number is less than the - width of the equation number), we either center it in the remaining space - or move it as far from the equation number as possible. The latter alternative - is taken only if the display begins with glue, since we assume that the - user put glue there to control the spacing precisely. - */ - d = half(line_w - eq_w); - if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ - d = half(line_w - eq_w - eqno_w); - if (p != null) - if (!is_char_node(p)) - if (type(p) == glue_node) - d = 0; - } - - tail_append(new_penalty(pre_display_penalty_par)); - if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */ - g1 = above_display_skip_code; - g2 = below_display_skip_code; - } else { - g1 = above_display_short_skip_code; - g2 = below_display_short_skip_code; - } - - /* If the equation number is set on a line by itself, either before or - after the formula, we append an infinite penalty so that no page break will - separate the display from its number; and we use the same size and - displacement for all three potential lines of the display, even though - `\.{\\parshape}' may specify them differently. - */ - /* \.{\\leqno} on a forced single line due to |width=0| */ - /* it follows that |type(a)=hlist_node| */ - - if (eqno_box && l && (eqno_w == 0)) { - /* if (math_direction_par==dir_TLT) { */ - shift_amount(eqno_box) = 0; - /* } else { */ - /* } */ - append_to_vlist(eqno_box,lua_key_index(equation_number)); - tail_append(new_penalty(inf_penalty)); - } else { - inject_display_skip_before(g1); - } - - if (eqno_w != 0) { - r = new_kern(line_w - eq_w - eqno_w - d); - if (l) { - if (swap_dir) { - if (math_direction_par==dir_TLT) { - /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 1\n"); -#endif - s = new_kern(width(r) + eqno_w); - try_couple_nodes(eqno_box,r); - try_couple_nodes(r,eq_box); - try_couple_nodes(eq_box,s); - } else { - /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 2\n"); -#endif - try_couple_nodes(eqno_box,r); - try_couple_nodes(r,eq_box); - } - } else { - if (math_direction_par==dir_TLT) { - /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 3\n"); -#endif - s = new_kern(width(r) + eqno_w); - } else { - /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 4\n"); -#endif - s = new_kern(width(r)); - } - try_couple_nodes(eqno_box,r); - try_couple_nodes(r,eq_box); - try_couple_nodes(eq_box,s); - } - eq_box = eqno_box; - } else { - if (swap_dir) { - if (math_direction_par==dir_TLT) { - /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 5\n"); -#endif - } else { - /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 6\n"); -#endif - } - try_couple_nodes(eq_box,r); - try_couple_nodes(r,eqno_box); - } else { - if (math_direction_par==dir_TLT) { - /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 7\n"); -#endif - s = new_kern(d); - } else { - /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */ -#ifdef DEBUG - fprintf(stderr, "\nDEBUG: CASE 8\n"); -#endif - s = new_kern(width(r) + eqno_w); - } - try_couple_nodes(s,eq_box); - try_couple_nodes(eq_box,r); - try_couple_nodes(r,eqno_box); - eq_box = s; - } - } - eq_box = hpack(eq_box, 0, additional, -1); - subtype(eq_box) = equation_list; /* new */ - build_attribute_list(eq_box); - shift_amount(eq_box) = line_s; - } else { - shift_amount(eq_box) = line_s + d; - } -/* check for prev: */ - append_to_vlist(eq_box,lua_key_index(equation)); - - if ((eqno_box != null) && (eqno_w == 0) && !l) { - tail_append(new_penalty(inf_penalty)); - /* if (math_direction_par==dir_TLT) { */ - shift_amount(eqno_box) = line_s + line_w - eqno_width ; - /* } else { */ - /* } */ - append_to_vlist(eqno_box,lua_key_index(equation_number)); - g2 = 0; - } - if (t != adjust_head) { /* migrating material comes after equation number */ - vlink(tail) = vlink(adjust_head); - /* needs testing */ - alink(adjust_tail) = alink(tail); - tail = t; - } - if (pre_t != pre_adjust_head) { - vlink(tail) = vlink(pre_adjust_head); - /* needs testing */ - alink(pre_adjust_tail) = alink(tail); - tail = pre_t; - } - tail_append(new_penalty(post_display_penalty_par)); - inject_display_skip_after(g2); - resume_after_display(); -} - -@ @c -void after_math(void) -{ - int m; /* |mmode| or |-mmode| */ - pointer p; /* the formula */ - pointer a = null; /* box containing equation number */ - boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ - m = mode; - p = fin_mlist(null); /* this pops the nest */ - if (cur_cmd == math_shift_cs_cmd && - (cur_chr == text_style || cur_chr == display_style)) { - you_cant(); - } - if (mode == -m) { /* end of equation number */ - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - run_mlist_to_hlist(p, false, text_style); - a = hpack(vlink(temp_head), 0, additional, -1); - build_attribute_list(a); - unsave_math(); - decr(save_ptr); /* now |cur_group=math_shift_group| */ - assert(saved_type(0) == saved_eqno); - if (saved_value(0) == 1) - l = true; - m = mode; - p = fin_mlist(null); - - } - if (m < 0) { - /* The |unsave| is done after everything else here; hence an appearance of - `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these - particular \.\$'s. This is consistent with the conventions of - `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the - space above that display. - */ - if (cur_cmd == math_shift_cs_cmd) { - check_inline_math_end(); - } - tail_append(new_math(math_surround_par, before)); - /* begin mathskip code */ - switch (math_skip_mode) { - case 0 : - /* obey mathsurround when zero glue */ - if (! glue_is_zero(math_skip_par)) { - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - } - break ; - case 1 : - /* always left */ - case 3 : - /* always both */ - case 6 : - /* only when skip */ - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - break ; - case 2 : - /* only right */ - surround(tail) = 0; - break ; - case 4 : - /* ignore, obey marthsurround */ - break ; - case 5: - /* all spacing disabled */ - surround(tail) = 0; - break ; - } - /* end mathskip code */ - if (dir_math_save) { - tail_append(new_dir(math_direction_par)); - } - run_mlist_to_hlist(p, (mode > 0), text_style); - vlink(tail) = vlink(temp_head); - while (vlink(tail) != null) { - tail = vlink(tail); - } - if (dir_math_save) { - tail_append(new_dir(math_direction_par - dir_swap)); - } - dir_math_save = false; - tail_append(new_math(math_surround_par, after)); - /* begin mathskip code */ - switch (math_skip_mode) { - case 0 : - /* obey mathsurround when zero glue */ - if (! glue_is_zero(math_skip_par)) { - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - } - break ; - case 2 : - /* always right */ - case 3 : - /* always both */ - case 6 : - /* only when skip */ - copy_glue_values(tail,math_skip_par); - surround(tail) = 0; - break ; - case 1 : - /* only left */ - surround(tail) = 0; - break ; - case 4 : - /* ignore, obey marthsurround */ - break ; - case 5: - /* all spacing disabled */ - surround(tail) = 0; - break ; - } - /* end mathskip code */ - space_factor_par = 1000; - unsave_math(); - } else { - if (a == null) { - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - } - run_mlist_to_hlist(p, false, display_style); - finish_displayed_math(l, a, vlink(temp_head)); - } -} - -@ When \.{\\halign} appears in a display, the alignment routines operate -essentially as they do in vertical mode. Then the following program is -activated, with |p| and |q| pointing to the beginning and end of the -resulting list, and with |aux_save| holding the |prev_depth| value. - -@c -void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) -{ - do_assignments(); - if (cur_cmd == math_shift_cmd) { - check_second_math_shift(); - } else { - check_display_math_end(); - } - pop_nest(); - tail_append(new_penalty(pre_display_penalty_par)); - inject_display_skip_before(above_display_skip_code); - vlink(tail) = p; - if (p != null) - tail = q; - tail_append(new_penalty(post_display_penalty_par)); - inject_display_skip_after(below_display_skip_code); - cur_list.prev_depth_field = saved_prevdepth; - resume_after_display(); -} - -@ Interface to \.{\\Umath} and \.{\\mathstyle} - -@c -void setup_math_style(void) -{ - pointer q; - tail_append(new_noad()); - q = new_node(math_char_node, 0); - nucleus(tail) = q; - (void) scan_math_style(nucleus(tail), num_style(m_style)); -} - -@ @c -void print_math_style(void) -{ - if (abs(mode) == mmode) - print_int(m_style); - else - print_int(-1); -} +% texmath.w +% +% Copyright 2008-2010 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c +#include "ptexlib.h" + +@ @c +#define mode mode_par +#define tail tail_par +#define head head_par +#define dir_save dirs_par + +/* + + \mathdisplayskipmode + + tex normally always inserts before and only after when larger than zero + + 0 = normal tex + 1 = always + 2 = non-zero + 3 = ignore + +*/ + +@ TODO: not sure if this is the right order +@c +#define back_error(A,B) do { \ + OK_to_interrupt=false; \ + back_input(); \ + OK_to_interrupt=true; \ + tex_error(A,B); \ + } while (0) + +@ @c +int scan_math(pointer, int); +int scan_math_style(pointer, int); +pointer fin_mlist(pointer); + +@ When \TeX\ reads a formula that is enclosed between \.\$'s, it constructs an +{\sl mlist}, which is essentially a tree structure representing that +formula. An mlist is a linear sequence of items, but we can regard it as +a tree structure because mlists can appear within mlists. For example, many +of the entries can be subscripted or superscripted, and such ``scripts'' +are mlists in their own right. + +An entire formula is parsed into such a tree before any of the actual +typesetting is done, because the current style of type is usually not +known until the formula has been fully scanned. For example, when the +formula `\.{\$a+b \\over c+d\$}' is being read, there is no way to tell +that `\.{a+b}' will be in script size until `\.{\\over}' has appeared. + +During the scanning process, each element of the mlist being built is +classified as a relation, a binary operator, an open parenthesis, etc., +or as a construct like `\.{\\sqrt}' that must be built up. This classification +appears in the mlist data structure. + +After a formula has been fully scanned, the mlist is converted to an hlist +so that it can be incorporated into the surrounding text. This conversion is +controlled by a recursive procedure that decides all of the appropriate +styles by a ``top-down'' process starting at the outermost level and working +in towards the subformulas. The formula is ultimately pasted together using +combinations of horizontal and vertical boxes, with glue and penalty nodes +inserted as necessary. + +An mlist is represented internally as a linked list consisting chiefly +of ``noads'' (pronounced ``no-adds''), to distinguish them from the somewhat +similar ``nodes'' in hlists and vlists. Certain kinds of ordinary nodes are +allowed to appear in mlists together with the noads; \TeX\ tells the difference +by means of the |type| field, since a noad's |type| is always greater than +that of a node. An mlist does not contain character nodes, hlist nodes, vlist +nodes, math nodes or unset nodes; in particular, each mlist item appears in the +variable-size part of |mem|, so the |type| field is always present. + +Each noad is five or more words long. The first word contains the +|type| and |subtype| and |link| fields that are already so familiar to +us; the second contains the attribute list pointer, and the third, +fourth an fifth words are called the noad's |nucleus|, |subscr|, and +|supscr| fields. (This use of a combined attribute list is temporary. +Eventually, each of fields need their own list) + +Consider, for example, the simple formula `\.{\$x\^2\$}', which would be +parsed into an mlist containing a single element called an |ord_noad|. +The |nucleus| of this noad is a representation of `\.x', the |subscr| is +empty, and the |supscr| is a representation of `\.2'. + +The |nucleus|, |subscr|, and |supscr| fields are further broken into +subfields. If |p| points to a noad, and if |q| is one of its principal +fields (e.g., |q=subscr(p)|), |q=null| indicates a field with no value (the +corresponding attribute of noad |p| is not present). Otherwise, there are +several possibilities for the subfields, depending on the |type| of |q|. + +\yskip\hang|type(q)=math_char_node| means that |math_fam(q)| refers to one of +the sixteen font families, and |character(q)| is the number of a character +within a font of that family, as in a character node. + +\yskip\hang|type(q)=math_text_char_node| is similar, but the character is +unsubscripted and unsuperscripted and it is followed immediately by another +character from the same font. (This |type| setting appears only +briefly during the processing; it is used to suppress unwanted italic +corrections.) + +\yskip\hang|type(q)=sub_box_node| means that |math_list(q)| points to a box +node (either an |hlist_node| or a |vlist_node|) that should be used as the +value of the field. The |shift_amount| in the subsidiary box node is the +amount by which that box will be shifted downward. + +\yskip\hang|type(q)=sub_mlist_node| means that |math_list(q)| points to +an mlist; the mlist must be converted to an hlist in order to obtain +the value of this field. + +\yskip\noindent In the latter case, we might have |math_list(q)=null|. This +is not the same as |q=null|; for example, `\.{\$P\_\{\}\$}' +and `\.{\$P\$}' produce different results (the former will not have the +``italic correction'' added to the width of |P|, but the ``script skip'' +will be added). + +@c +static void unsave_math(void) +{ + unsave(); + decr(save_ptr); + flush_node_list(text_dir_ptr); + assert(saved_type(0) == saved_textdir); + text_dir_ptr = saved_value(0); +} + +@ Sometimes it is necessary to destroy an mlist. The following +subroutine empties the current list, assuming that |abs(mode)=mmode|. + +@c +void flush_math(void) +{ + flush_node_list(vlink(head)); + flush_node_list(incompleat_noad_par); + vlink(head) = null; + tail = head; + incompleat_noad_par = null; +} + +@ Before we can do anything in math mode, we need fonts. + +@c +#define MATHFONTSTACK 8 +#define MATHFONTDEFAULT 0 /* == nullfont */ + +static sa_tree math_fam_head = NULL; + +@ @c +int fam_fnt(int fam_id, int size_id) +{ + int n = fam_id + (256 * size_id); + return (int) get_sa_item(math_fam_head, n).int_value; +} + +void def_fam_fnt(int fam_id, int size_id, int f, int lvl) +{ + int n = fam_id + (256 * size_id); + sa_tree_item sa_value = { 0 }; + sa_value.int_value = f; + set_sa_item(math_fam_head, n, sa_value, lvl); + fixup_math_parameters(fam_id, size_id, f, lvl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_cmd_chr(def_family_cmd, size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } +} + +@ @c +static void unsave_math_fam_data(int gl) +{ + sa_stack_item st; + if (math_fam_head->stack == NULL) + return; + while (math_fam_head->stack_ptr > 0 && + abs(math_fam_head->stack[math_fam_head->stack_ptr].level) + >= (int) gl) { + st = math_fam_head->stack[math_fam_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_fam_head, st.code, st.value); + /* now do a trace message, if requested */ + if (tracing_restores_par > 1) { + int size_id = st.code / 256; + int fam_id = st.code % 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_cmd_chr(def_family_cmd, size_id); + print_int(fam_id); + print_char('='); + print_font_identifier(fam_fnt(fam_id, size_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_fam_head->stack_ptr)--; + } +} + +@ and parameters + +@c +#define MATHPARAMSTACK 8 +#define MATHPARAMDEFAULT undefined_math_parameter + +static sa_tree math_param_head = NULL; + +@ @c +void def_math_param(int param_id, int style_id, scaled value, int lvl) +{ + int n = param_id + (256 * style_id); + sa_tree_item sa_value = { 0 }; + sa_value.int_value = (int) value; + set_sa_item(math_param_head, n, sa_value, lvl); + if (tracing_assigns_par > 1) { + begin_diagnostic(); + tprint("{assigning"); + print_char(' '); + print_cmd_chr(set_math_param_cmd, param_id); + print_cmd_chr(math_style_cmd, style_id); + print_char('='); + print_int(value); + print_char('}'); + end_diagnostic(false); + } +} + +scaled get_math_param(int param_id, int style_id) +{ + int n = param_id + (256 * style_id); + return (scaled) get_sa_item(math_param_head, n).int_value; +} + +@ @c +static void unsave_math_param_data(int gl) +{ + sa_stack_item st; + if (math_param_head->stack == NULL) + return; + while (math_param_head->stack_ptr > 0 && + abs(math_param_head->stack[math_param_head->stack_ptr].level) + >= (int) gl) { + st = math_param_head->stack[math_param_head->stack_ptr]; + if (st.level > 0) { + rawset_sa_item(math_param_head, st.code, st.value); + /* now do a trace message, if requested */ + if (tracing_restores_par > 1) { + int param_id = st.code % 256; + int style_id = st.code / 256; + begin_diagnostic(); + tprint("{restoring"); + print_char(' '); + print_cmd_chr(set_math_param_cmd, param_id); + print_cmd_chr(math_style_cmd, style_id); + print_char('='); + print_int(get_math_param(param_id, style_id)); + print_char('}'); + end_diagnostic(false); + } + } + (math_param_head->stack_ptr)--; + } +} + +@ saving and unsaving of both + +@c +void unsave_math_data(int gl) +{ + unsave_math_fam_data(gl); + unsave_math_param_data(gl); +} + +@ Dumping and undumping +@c +void dump_math_data(void) +{ + sa_tree_item sa_value = { 0 }; + if (math_fam_head == NULL) { + sa_value.int_value = MATHFONTDEFAULT; + math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); + } + dump_sa_tree(math_fam_head, "mathfonts"); + if (math_param_head == NULL) { + sa_value.int_value = MATHPARAMDEFAULT; + math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); + } + dump_sa_tree(math_param_head, "mathparameters"); +} + +void undump_math_data(void) +{ + math_fam_head = undump_sa_tree("mathfonts"); + math_param_head = undump_sa_tree("mathparameters"); +} + +@ @c +void initialize_math(void) +{ + sa_tree_item sa_value = { 0 }; + if (math_fam_head == NULL) { + sa_value.int_value = MATHFONTDEFAULT; + math_fam_head = new_sa_tree(MATHFONTSTACK, 1, sa_value); + } + if (math_param_head == NULL) { + sa_value.int_value = MATHPARAMDEFAULT; + math_param_head = new_sa_tree(MATHPARAMSTACK, 1, sa_value); + initialize_math_spacing(); + } + return; +} + +@ Each portion of a formula is classified as Ord, Op, Bin, Rel, Ope, +Clo, Pun, or Inn, for purposes of spacing and line breaking. An +|ord_noad|, |op_noad|, |bin_noad|, |rel_noad|, |open_noad|, |close_noad|, +|punct_noad|, or |inner_noad| is used to represent portions of the various +types. For example, an `\.=' sign in a formula leads to the creation of a +|rel_noad| whose |nucleus| field is a representation of an equals sign +(usually |fam=0|, |character=075|). A formula preceded by \.{\\mathrel} +also results in a |rel_noad|. When a |rel_noad| is followed by an +|op_noad|, say, and possibly separated by one or more ordinary nodes (not +noads), \TeX\ will insert a penalty node (with the current |rel_penalty|) +just after the formula that corresponds to the |rel_noad|, unless there +already was a penalty immediately following; and a ``thick space'' will be +inserted just before the formula that corresponds to the |op_noad|. + +A noad of type |ord_noad|, |op_noad|, \dots, |inner_noad| usually +has a |subtype=normal|. The only exception is that an |op_noad| might +have |subtype=limits| or |no_limits|, if the normal positioning of +limits has been overridden for this operator. + +A |radical_noad| also has a |left_delimiter| field, which usually +represents a square root sign. + +A |fraction_noad| has a |right_delimiter| field as well as a |left_delimiter|. + +Delimiter fields have four subfields +called |small_fam|, |small_char|, |large_fam|, |large_char|. These subfields +represent variable-size delimiters by giving the ``small'' and ``large'' +starting characters, as explained in Chapter~17 of {\sl The \TeX book}. +@:TeXbook}{\sl The \TeX book@> + +A |fraction_noad| is actually quite different from all other noads. +It has |thickness|, |denominator|, and |numerator| fields instead of +|nucleus|, |subscr|, and |supscr|. The |thickness| is a scaled value +that tells how thick to make a fraction rule; however, the special +value |default_code| is used to stand for the +|default_rule_thickness| of the current size. The |numerator| and +|denominator| point to mlists that define a fraction; we always have +$$\hbox{|type(numerator)=type(denominator)=sub_mlist|}.$$ The +|left_delimiter| and |right_delimiter| fields specify delimiters that will +be placed at the left and right of the fraction. In this way, a +|fraction_noad| is able to represent all of \TeX's operators \.{\\over}, +\.{\\atop}, \.{\\above}, \.{\\overwithdelims}, \.{\\atopwithdelims}, and + \.{\\abovewithdelims}. + +@ The |new_noad| function creates an |ord_noad| that is completely null + +@c +pointer new_noad(void) +{ + pointer p; + p = new_node(simple_noad, ord_noad_type); + /* all noad fields are zero after this */ + return p; +} + +@ @c +pointer new_sub_box(pointer curbox) +{ + pointer p, q; + p = new_noad(); + q = new_node(sub_box_node, 0); + nucleus(p) = q; + math_list(nucleus(p)) = curbox; + return p; +} + +@ A few more kinds of noads will complete the set: An |under_noad| has its +nucleus underlined; an |over_noad| has it overlined. An |accent_noad| places +an accent over its nucleus; the accent character appears as +|math_fam(accent_chr(p))| and |math_character(accent_chr(p))|. A |vcenter_noad| +centers its nucleus vertically with respect to the axis of the formula; +in such noads we always have |type(nucleus(p))=sub_box|. + +And finally, we have the |fence_noad| type, to implement +\TeX's \.{\\left} and \.{\\right} as well as eTeX's \.{\\middle}. +The |nucleus| of such noads is +replaced by a |delimiter| field; thus, for example, `\.{\\left(}' produces +a |fence_noad| such that |delimiter(p)| holds the family and character +codes for all left parentheses. A |fence_noad| of subtype |left_noad_side| +never appears in an mlist except as the first element, and a |fence_noad| +with subtype |right_noad_side| never appears in an mlist +except as the last element; furthermore, we either have both a |left_noad_side| +and a |right_noad_side|, or neither one is present. + +@ Math formulas can also contain instructions like \.{\\textstyle} that +override \TeX's normal style rules. A |style_node| is inserted into the +data structure to record such instructions; it is three words long, so it +is considered a node instead of a noad. The |subtype| is either |display_style| +or |text_style| or |script_style| or |script_script_style|. The +second and third words of a |style_node| are not used, but they are +present because a |choice_node| is converted to a |style_node|. + +\TeX\ uses even numbers 0, 2, 4, 6 to encode the basic styles +|display_style|, \dots, |script_script_style|, and adds~1 to get the +``cramped'' versions of these styles. This gives a numerical order that +is backwards from the convention of Appendix~G in {\sl The \TeX book\/}; +i.e., a smaller style has a larger numerical value. +@:TeXbook}{\sl The \TeX book@> + +@c +const char *math_style_names[] = { + "display", "crampeddisplay", + "text", "crampedtext", + "script", "crampedscript", + "scriptscript", "crampedscriptscript", + NULL +}; + +const char *math_param_names[] = { + "quad", "axis", "operatorsize", + "overbarkern", "overbarrule", "overbarvgap", + "underbarkern", "underbarrule", "underbarvgap", + "radicalkern", "radicalrule", "radicalvgap", + "radicaldegreebefore", "radicaldegreeafter", "radicaldegreeraise", + "stackvgap", "stacknumup", "stackdenomdown", + "fractionrule", "fractionnumvgap", "fractionnumup", + "fractiondenomvgap", "fractiondenomdown", "fractiondelsize", + "limitabovevgap", "limitabovebgap", "limitabovekern", + "limitbelowvgap", "limitbelowbgap", "limitbelowkern", + "nolimitsubfactor", "nolimitsupfactor", /* bonus */ + "underdelimitervgap", "underdelimiterbgap", + "overdelimitervgap", "overdelimiterbgap", + "subshiftdrop", "supshiftdrop", "subshiftdown", + "subsupshiftdown", "subtopmax", "supshiftup", + "supbottommin", "supsubbottommax", "subsupvgap", + "spaceafterscript", "connectoroverlapmin", + "ordordspacing", "ordopspacing", "ordbinspacing", "ordrelspacing", + "ordopenspacing", "ordclosespacing", "ordpunctspacing", "ordinnerspacing", + "opordspacing", "opopspacing", "opbinspacing", "oprelspacing", + "opopenspacing", "opclosespacing", "oppunctspacing", "opinnerspacing", + "binordspacing", "binopspacing", "binbinspacing", "binrelspacing", + "binopenspacing", "binclosespacing", "binpunctspacing", "bininnerspacing", + "relordspacing", "relopspacing", "relbinspacing", "relrelspacing", + "relopenspacing", "relclosespacing", "relpunctspacing", "relinnerspacing", + "openordspacing", "openopspacing", "openbinspacing", "openrelspacing", + "openopenspacing", "openclosespacing", "openpunctspacing", + "openinnerspacing", + "closeordspacing", "closeopspacing", "closebinspacing", "closerelspacing", + "closeopenspacing", "closeclosespacing", "closepunctspacing", + "closeinnerspacing", + "punctordspacing", "punctopspacing", "punctbinspacing", "punctrelspacing", + "punctopenspacing", "punctclosespacing", "punctpunctspacing", + "punctinnerspacing", + "innerordspacing", "inneropspacing", "innerbinspacing", "innerrelspacing", + "inneropenspacing", "innerclosespacing", "innerpunctspacing", + "innerinnerspacing", + NULL +}; + +@ @c +pointer new_style(small_number s) +{ /* create a style node */ + m_style = s; + return new_node(style_node, s); +} + +@ Finally, the \.{\\mathchoice} primitive creates a |choice_node|, which +has special subfields |display_mlist|, |text_mlist|, |script_mlist|, +and |script_script_mlist| pointing to the mlists for each style. + +@c +static pointer new_choice(void) +{ /* create a choice node */ + return new_node(choice_node, 0); /* the |subtype| is not used */ +} + +@ Let's consider now the previously unwritten part of |show_node_list| +that displays the things that can only be present in mlists; this +program illustrates how to access the data structures just defined. + +In the context of the following program, |p| points to a node or noad that +should be displayed, and the current string contains the ``recursion history'' +that leads to this point. The recursion history consists of a dot for each +outer level in which |p| is subsidiary to some node, or in which |p| is +subsidiary to the |nucleus| field of some noad; the dot is replaced by +`\.\_' or `\.\^' or `\./' or `\.\\' if |p| is descended from the |subscr| +or |supscr| or |denominator| or |numerator| fields of noads. For example, +the current string would be `\.{.\^.\_/}' if |p| points to the |ord_noad| for +|x| in the (ridiculous) formula +`\.{\$\\sqrt\{a\^\{\\mathinner\{b\_\{c\\over x+y\}\}\}\}\$}'. + +@c +void display_normal_noad(pointer p); /* forward */ +void display_fence_noad(pointer p); /* forward */ +void display_fraction_noad(pointer p); /* forward */ + +void show_math_node(pointer p) +{ + switch (type(p)) { + case style_node: + print_cmd_chr(math_style_cmd, subtype(p)); + break; + case choice_node: + tprint_esc("mathchoice"); + append_char('D'); + show_node_list(display_mlist(p)); + flush_char(); + append_char('T'); + show_node_list(text_mlist(p)); + flush_char(); + append_char('S'); + show_node_list(script_mlist(p)); + flush_char(); + append_char('s'); + show_node_list(script_script_mlist(p)); + flush_char(); + break; + case simple_noad: + case radical_noad: + case accent_noad: + display_normal_noad(p); + break; + case fence_noad: + display_fence_noad(p); + break; + case fraction_noad: + display_fraction_noad(p); + break; + default: + tprint("Unknown node type!"); + break; + } +} + +@ Here are some simple routines used in the display of noads. + +@c +static void print_fam_and_char(pointer p) +{ /* prints family and character */ + tprint_esc("fam"); + print_int(math_fam(p)); + print_char(' '); + print(math_character(p)); +} + +@ @c +static void print_delimiter(pointer p) +{ + int a; + if (delimiteroptionset(p)) { + tprint(" [ "); + if (delimiteraxis(p)) + tprint("axis "); + if (delimiternoaxis(p)) + tprint("noaxis "); + if (delimiterexact(p)) + tprint("exact "); + tprint("]"); + } + if (delimiterheight(p)) { + tprint("height="); + print_scaled(delimiterheight(p)); + tprint(" "); + } + if (delimiterdepth(p)) { + tprint("depth="); + print_scaled(delimiterdepth(p)); + tprint(" "); + } + if (delimiterclass(p)) { + tprint("class="); + print_int(delimiterclass(p)); + tprint(" "); + } + if (small_fam(p) < 0) { + print_int(-1); /* this should never happen */ + } else if (small_fam(p) < 16 && large_fam(p) < 16 && + small_char(p) < 256 && large_char(p) < 256) { + /* traditional tex style */ + a = small_fam(p) * 256 + small_char(p); + a = a * 0x1000 + large_fam(p) * 256 + large_char(p); + print_hex(a); + } else if ((large_fam(p) == 0 && large_char(p) == 0) || + small_char(p) > 65535 || large_char(p) > 65535) { + /* modern xetex/luatex style */ + print_hex(small_fam(p)); + print_hex(small_char(p)); + } +} + +@ The next subroutine will descend to another level of recursion when a +subsidiary mlist needs to be displayed. The parameter |c| indicates what +character is to become part of the recursion history. An empty mlist is +distinguished from a missing field, because these are not equivalent +(as explained above). +@^recursion@> + +@c +static void print_subsidiary_data(pointer p, ASCII_code c) +{ /* display a noad field */ + if ((int) cur_length >= depth_threshold) { + if (p != null) + tprint(" []"); + } else { + append_char(c); /* include |c| in the recursion history */ + if (p != null) { + switch (type(p)) { + case math_char_node: + print_ln(); + print_current_string(); + print_fam_and_char(p); + break; + case sub_box_node: + show_node_list(math_list(p)); + break; + case sub_mlist_node: + if (math_list(p) == null) { + print_ln(); + print_current_string(); + tprint("{}"); + } else { + show_node_list(math_list(p)); + } + break; + } + } + flush_char(); /* remove |c| from the recursion history */ + } +} + +@ @c +void display_normal_noad(pointer p) +{ + switch (type(p)) { + case simple_noad: + switch (subtype(p)) { + case ord_noad_type: + tprint_esc("mathord"); + break; + case op_noad_type_normal: + case op_noad_type_limits: + case op_noad_type_no_limits: + tprint_esc("mathop"); + if (subtype(p) == op_noad_type_limits) + tprint_esc("limits"); + else if (subtype(p) == op_noad_type_no_limits) + tprint_esc("nolimits"); + break; + case bin_noad_type: + tprint_esc("mathbin"); + break; + case rel_noad_type: + tprint_esc("mathrel"); + break; + case open_noad_type: + tprint_esc("mathopen"); + break; + case close_noad_type: + tprint_esc("mathclose"); + break; + case punct_noad_type: + tprint_esc("mathpunct"); + break; + case inner_noad_type: + tprint_esc("mathinner"); + break; + case over_noad_type: + tprint_esc("overline"); + break; + case under_noad_type: + tprint_esc("underline"); + break; + case vcenter_noad_type: + tprint_esc("vcenter"); + break; + default: + tprint(""); + break; + } + break; + case radical_noad: + if (subtype(p) == 6) + tprint_esc("Udelimiterover"); + else if (subtype(p) == 5) + tprint_esc("Udelimiterunder"); + else if (subtype(p) == 4) + tprint_esc("Uoverdelimiter"); + else if (subtype(p) == 3) + tprint_esc("Uunderdelimiter"); + else if (subtype(p) == 2) + tprint_esc("Uroot"); + else + tprint_esc("radical"); + print_delimiter(left_delimiter(p)); + if (degree(p) != null) { + print_subsidiary_data(degree(p), '/'); + } + if (radicalwidth(p)) { + tprint("width="); + print_scaled(radicalwidth(p)); + tprint(" "); + } + if (radicaloptionset(p)) { + tprint(" [ "); + if (radicalexact(p)) + tprint("exact "); + if (radicalleft(p)) + tprint("left "); + if (radicalmiddle(p)) + tprint("middle "); + if (radicalright(p)) + tprint("right "); + tprint("]"); + } + break; + case accent_noad: + if (top_accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccent both"); + } else { + tprint_esc("Umathaccent"); + } + } else if (bot_accent_chr(p) != null) { + tprint_esc("Umathaccent bottom"); + } else { + tprint_esc("Umathaccent overlay"); + } + if (accentfraction(p)) { + tprint(" fraction="); + print_int(accentfraction(p)); + tprint(" "); + } + switch (subtype(p)) { + case 0: + if (top_accent_chr(p) != null) { + if (bot_accent_chr(p) != null) { + print_fam_and_char(top_accent_chr(p)); + print_fam_and_char(bot_accent_chr(p)); + } else { + print_fam_and_char(top_accent_chr(p)); + } + } else if (bot_accent_chr(p) != null) { + print_fam_and_char(bot_accent_chr(p)); + } else { + print_fam_and_char(overlay_accent_chr(p)); + } + break; + case 1: + if (top_accent_chr(p) != null) { + tprint(" fixed "); + print_fam_and_char(top_accent_chr(p)); + if (bot_accent_chr(p) != null) { + print_fam_and_char(bot_accent_chr(p)); + } + } else { + confusion("display_accent_noad"); + } + break; + case 2: + if (bot_accent_chr(p) != null) { + if (top_accent_chr(p) != null) { + print_fam_and_char(top_accent_chr(p)); + } + tprint(" fixed "); + print_fam_and_char(bot_accent_chr(p)); + } else{ + confusion("display_accent_noad"); + } + break; + case 3: + if (top_accent_chr(p) != null && bot_accent_chr(p) != null) { + tprint(" fixed "); + print_fam_and_char(top_accent_chr(p)); + tprint(" fixed "); + print_fam_and_char(bot_accent_chr(p)); + } else { + confusion("display_accent_noad"); + } + break; + } + break; + } + print_subsidiary_data(nucleus(p), '.'); + print_subsidiary_data(supscr(p), '^'); + print_subsidiary_data(subscr(p), '_'); +} + +@ @c +void display_fence_noad(pointer p) +{ + if (subtype(p) == right_noad_side) + tprint_esc("right"); + else if (subtype(p) == left_noad_side) + tprint_esc("left"); + else + tprint_esc("middle"); + print_delimiter(delimiter(p)); +} + +@ @c +void display_fraction_noad(pointer p) +{ + tprint_esc("fraction, thickness "); + if (thickness(p) == default_code) + tprint("= default"); + else + print_scaled(thickness(p)); + if ((left_delimiter(p) != null) && + ((small_fam(left_delimiter(p)) != 0) || + (small_char(left_delimiter(p)) != 0) || + (large_fam(left_delimiter(p)) != 0) || + (large_char(left_delimiter(p)) != 0))) { + tprint(", left-delimiter "); + print_delimiter(left_delimiter(p)); + } + if ((right_delimiter(p) != null) && + ((small_fam(right_delimiter(p)) != 0) || + (small_char(right_delimiter(p)) != 0) || + (large_fam(right_delimiter(p)) != 0) || + (large_char(right_delimiter(p)) != 0))) { + tprint(", right-delimiter "); + print_delimiter(right_delimiter(p)); + } + print_subsidiary_data(numerator(p), '\\'); + print_subsidiary_data(denominator(p), '/'); +} + +@ The routines that \TeX\ uses to create mlists are similar to those we have +just seen for the generation of hlists and vlists. But it is necessary to +make ``noads'' as well as nodes, so the reader should review the +discussion of math mode data structures before trying to make sense out of +the following program. + +Here is a little routine that needs to be done whenever a subformula +is about to be processed. The parameter is a code like |math_group|. + +@c +static void new_save_level_math(group_code c) +{ + set_saved_record(0, saved_textdir, 0, text_dir_ptr); + text_dir_ptr = new_dir(math_direction_par); + incr(save_ptr); + new_save_level(c); + eq_word_define(int_base + body_direction_code, math_direction_par); + eq_word_define(int_base + par_direction_code, math_direction_par); + eq_word_define(int_base + text_direction_code, math_direction_par); +} + +@ @c +static void push_math(group_code c, int mstyle) +{ + if (math_direction_par != text_direction_par) + dir_math_save = true; + push_nest(); + mode = -mmode; + incompleat_noad_par = null; + m_style = mstyle; + new_save_level_math(c); +} + +@ @c +static void enter_ordinary_math(void) +{ + push_math(math_shift_group, text_style); + eq_word_define(int_base + cur_fam_code, -1); + if (every_math_par != null) + begin_token_list(every_math_par, every_math_text); +} + +@ @c +void enter_display_math(void); + +@ We get into math mode from horizontal mode when a `\.\$' (i.e., a +|math_shift| character) is scanned. We must check to see whether this +`\.\$' is immediately followed by another, in case display math mode is +called for. + +@c +void init_math(void) +{ + if (cur_cmd == math_shift_cmd) { + get_token(); /* |get_x_token| would fail on \.{\\ifmmode}\thinspace! */ + if ((cur_cmd == math_shift_cmd) && (mode > 0)) { + enter_display_math(); + } else { + back_input(); + enter_ordinary_math(); + } + } else if (cur_cmd == math_shift_cs_cmd && cur_chr == display_style && (mode > 0)) { + enter_display_math(); + } else if (cur_cmd == math_shift_cs_cmd && cur_chr == text_style) { + enter_ordinary_math(); + } else { + you_cant(); + } +} + +@ We get into ordinary math mode from display math mode when `\.{\\eqno}' or +`\.{\\leqno}' appears. In such cases |cur_chr| will be 0 or~1, respectively; +the value of |cur_chr| is placed onto |save_stack| for safe keeping. + +@ When \TeX\ is in display math mode, |cur_group=math_shift_group|, +so it is not necessary for the |start_eq_no| procedure to test for +this condition. + +@c +void start_eq_no(void) +{ + set_saved_record(0, saved_eqno, 0, cur_chr); + incr(save_ptr); + enter_ordinary_math(); +} + +@ Subformulas of math formulas cause a new level of math mode to be entered, +on the semantic nest as well as the save stack. These subformulas arise in +several ways: (1)~A left brace by itself indicates the beginning of a +subformula that will be put into a box, thereby freezing its glue and +preventing line breaks. (2)~A subscript or superscript is treated as a +subformula if it is not a single character; the same applies to +the nucleus of things like \.{\\underline}. (3)~The \.{\\left} primitive +initiates a subformula that will be terminated by a matching \.{\\right}. +The group codes placed on |save_stack| in these three cases are +|math_group|, |math_group|, and |math_left_group|, respectively. + +Here is the code that handles case (1); the other cases are not quite as +trivial, so we shall consider them later. + +@c +void math_left_brace(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + back_input(); + (void) scan_math(nucleus(tail), m_style); +} + +@ If the inline directions of \.{\\pardir} and \.{\\mathdir} are +opposite, then this function will return true. Discovering that fact +is somewhat odd because it needs traversal of the |save_stack|. +The occurance of displayed equations is weird enough that this is +probably still better than having yet another field in the |input_stack| +structures. + +None of this makes much sense if the inline direction of either one of +\.{\\pardir} or \.{\\mathdir} is vertical, but in that case the current +math machinery is ill suited anyway so I do not bother to test that. + +@c +static boolean math_and_text_reversed_p(void) +{ + int i = save_ptr - 1; + while (save_type(i) != level_boundary) + i--; + while (i < save_ptr) { + if (save_type(i) == restore_old_value && + save_value(i) == int_base + par_direction_code) { + if (textdir_opposite(math_direction_par, save_value(i - 1))) + return true; + } + i++; + } + return false; +} + +@ When we enter display math mode, we need to call |line_break| to +process the partial paragraph that has just been interrupted by the +display. Then we can set the proper values of |display_width| and +|display_indent| and |pre_display_size|. + +@c +void enter_display_math(void) +{ + scaled w; /* new or partial |pre_display_size| */ + scaled l; /* new |display_width| */ + scaled s; /* new |display_indent| */ + pointer p; + int n; /* scope of paragraph shape specification */ + if (head == tail || /* `\.{\\noindent\$\$}' or `\.{\$\${ }\$\$}' */ + (vlink(head) == tail && /* the 2nd of \.{\$\${ }\$\$} \.{\$\${ }\$\$} */ + type(tail) == local_par_node && vlink(tail) == null)) { + if (vlink(head) == tail) { + /* bug \#270: |resume_after_display| inserts a |local_par_node|, but if + there is another display immediately following, we have to get rid + of that node */ + flush_node(tail); + } + pop_nest(); + w = -max_dimen; + } else { + line_break(true, math_shift_group); + w = actual_box_width(just_box, x_over_n(quad(get_cur_font()),1000) * math_pre_display_gap_factor_par); + } + /* now we are in vertical mode, working on the list that will contain the display */ + /* A displayed equation is considered to be three lines long, so we + calculate the length and offset of line number |prev_graf+2|. */ + if (par_shape_par_ptr == null) { + if ((hang_indent_par != 0) && (((hang_after_par >= 0) && (prev_graf_par + 2 > hang_after_par)) || (prev_graf_par + 1 < -hang_after_par))) { + halfword used_hang_indent = swap_hang_indent(hang_indent_par); + l = hsize_par - abs(used_hang_indent); + if (used_hang_indent > 0) + s = used_hang_indent; + else + s = 0; + } else { + l = hsize_par; + s = 0; + } + } else { + n = vinfo(par_shape_par_ptr + 1); + if (prev_graf_par + 2 >= n) + p = par_shape_par_ptr + 2 * n + 1; + else + p = par_shape_par_ptr + 2 * (prev_graf_par + 2) + 1; + s = varmem[(p - 1)].cint; + l = varmem[p].cint; + s = swap_parshape_indent(s,l); + } + + push_math(math_shift_group, display_style); + mode = mmode; + eq_word_define(int_base + cur_fam_code, -1); + eq_word_define(dimen_base + pre_display_size_code, w); + eq_word_define(dimen_base + display_width_code, l); + eq_word_define(dimen_base + display_indent_code, s); + eq_word_define(int_base + pre_display_direction_code, (math_and_text_reversed_p() ? -1 : 0)); + if (every_display_par != null) + begin_token_list(every_display_par, every_display_text); + if (nest_ptr == 1) { + checked_page_filter(before_display); + build_page(); + } +} + +@ The next routine parses all variations of a delimiter code. The |extcode| + tells what syntax form to use (\TeX, XeTeX, XeTeXnum, ...) , the + |doclass| tells whether or not read a math class also (for \.{\\delimiter} c.s.). + (the class is passed on for conversion to \.{\\mathchar}). + +@c +static delcodeval do_scan_extdef_del_code(int extcode, boolean doclass) +{ + const char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + delcodeval d; + int mcls = 0, msfam = 0, mschr = 0, mlfam = 0, mlchr = 0; + if (extcode == tex_mathcode) { /* \.{\\delcode}, this is the easiest */ + scan_int(); + /* "MFCCFCC or "FCCFCC */ + if (doclass) { + mcls = (cur_val / 0x1000000); + cur_val = (cur_val & 0xFFFFFF); + } + if (cur_val > 0xFFFFFF) { + tex_error("Invalid delimiter code", hlp); + cur_val = 0; + } + msfam = (cur_val / 0x100000); + mschr = (cur_val % 0x100000) / 0x1000; + mlfam = (cur_val & 0xFFF) / 0x100; + mlchr = (cur_val % 0x100); + } else if (extcode == umath_mathcode) { /* \.{\\Udelcode} */ + /* <0-7>,<0-0xFF>,<0-0x10FFFF> or <0-0xFF>,<0-0x10FFFF> */ + if (doclass) { + scan_int(); + mcls = cur_val; + } + scan_int(); + msfam = cur_val; + scan_char_num(); + mschr = cur_val; + if (msfam < 0 || msfam > 255) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else if (extcode == umathnum_mathcode) { /* \.{\\Udelcodenum} */ + /* "FF<21bits> */ + /* the largest numeric value is $2^29-1$, but + the top of bit 21 can't be used as it contains invalid USV's + */ + if (doclass) { /* such a primitive doesn't exist */ + confusion("umathnum_mathcode"); + } + scan_int(); + msfam = (cur_val / 0x200000); + mschr = cur_val & 0x1FFFFF; + if (msfam < 0 || msfam > 255 || mschr > 0x10FFFF) { + tex_error("Invalid delimiter code", hlp); + msfam = 0; + mschr = 0; + } + mlfam = 0; + mlchr = 0; + } else { + /* something's gone wrong */ + confusion("unknown_extcode"); + } + d.class_value = mcls; + d.small_family_value = msfam; + d.small_character_value = mschr; + d.large_family_value = mlfam; + d.large_character_value = mlchr; + return d; +} + +@ @c +void scan_extdef_del_code(int level, int extcode) +{ + delcodeval d; + int p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = do_scan_extdef_del_code(extcode, false); + set_del_code(p, d.small_family_value, d.small_character_value, + d.large_family_value, d.large_character_value, + (quarterword) (level)); +} + +@ @c +mathcodeval scan_mathchar(int extcode) +{ + char errstr[255] = { 0 }; + const char *hlp[] = { + "I'm going to use 0 instead of that illegal code value.", + NULL + }; + mathcodeval d; + int mcls = 0, mfam = 0, mchr = 0; + if (extcode == tex_mathcode) { /* \.{\\mathcode} */ + /* "TFCC */ + scan_int(); + if (cur_val > 0x8000) { + /* + tex_error("Invalid math code", hlp); + cur_val = 0; + */ + /* needed for latex: fallback to umathnum_mathcode */ + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code during > 0x8000 mathcode fallback", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + if (cur_val < 0) { + snprintf(errstr, 255, "Bad mathchar (%d)", (int)cur_val); + tex_error(errstr, hlp); + cur_val = 0; + } + mcls = (cur_val / 0x1000); + mfam = ((cur_val % 0x1000) / 0x100); + mchr = (cur_val % 0x100); + } + } else if (extcode == umath_mathcode) { + /* <0-0x7> <0-0xFF> <0-0x10FFFF> */ + scan_int(); + mcls = cur_val; + scan_int(); + mfam = cur_val; + scan_char_num(); + mchr = cur_val; + if (mcls < 0 || mcls > 7 || mfam > 255) { + tex_error("Invalid math code", hlp); + mchr = 0; + mfam = 0; + mcls = 0; + } + } else if (extcode == umathnum_mathcode) { + /* "FFT<21bits> */ + /* the largest numeric value is $2^32-1$, but + the top of bit 21 can't be used as it contains invalid USV's + */ + /* Note: |scan_int| won't accept families 128-255 because these use bit 32 */ + scan_int(); + mfam = (cur_val / 0x200000) & 0x7FF; + mcls = mfam % 0x08; + mfam = mfam / 0x08; + mchr = cur_val & 0x1FFFFF; + if (mchr > 0x10FFFF) { + tex_error("Invalid math code", hlp); + mcls = 0; + mfam = 0; + mchr = 0; + } + } else { + /* something's gone wrong */ + confusion("unknown_extcode"); + } + d.class_value = mcls; + d.family_value = mfam; + d.character_value = mchr; + return d; +} + +@ @c +void scan_extdef_math_code(int level, int extcode) +{ + mathcodeval d; + int p; + scan_char_num(); + p = cur_val; + scan_optional_equals(); + d = scan_mathchar(extcode); + set_math_code(p, d.class_value, + d.family_value, d.character_value, (quarterword) (level)); +} + +@ this reads in a delcode when actually a mathcode is needed +@c +mathcodeval scan_delimiter_as_mathchar(int extcode) +{ + delcodeval dval; + mathcodeval mval; + dval = do_scan_extdef_del_code(extcode, true); + mval.class_value = dval.class_value; + mval.family_value = dval.small_family_value; + mval.character_value = dval.small_character_value; + return mval; +} + +@ Recall that the |nucleus|, |subscr|, and |supscr| fields in a noad +are broken down into subfields called |type| and either |math_list| or +|(math_fam,math_character)|. The job of |scan_math| is to figure out +what to place in one of these principal fields; it looks at the +subformula that comes next in the input, and places an encoding of +that subformula into a given word of |mem|. + +@c +#define get_next_nb_nr() do { get_x_token(); } while (cur_cmd==spacer_cmd||cur_cmd==relax_cmd) + +int scan_math_style(pointer p, int mstyle) +{ + get_next_nb_nr(); + back_input(); + scan_left_brace(); + set_saved_record(0, saved_math, 0, p); + incr(save_ptr); + push_math(math_group, mstyle); + return 1; +} + +int scan_math(pointer p, int mstyle) +{ + /* label restart,reswitch,exit; */ + mathcodeval mval = { 0, 0, 0 }; + assert(p != null); + RESTART: + get_next_nb_nr(); + RESWITCH: + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + case char_given_cmd: + mval = get_math_code(cur_chr); + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + goto RESTART; + } + break; + case char_num_cmd: + scan_char_num(); + cur_chr = cur_val; + cur_cmd = char_given_cmd; + goto RESWITCH; + break; + case math_char_num_cmd: + if (cur_chr == 0) + mval = scan_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_mathchar(umath_mathcode); + else if (cur_chr == 2) + mval = scan_mathchar(umathnum_mathcode); + else + confusion("scan_math"); + break; + case math_given_cmd: + mval = mathchar_from_integer(cur_chr, tex_mathcode); + break; + case xmath_given_cmd: + mval = mathchar_from_integer(cur_chr, umath_mathcode); + break; + case delim_num_cmd: + if (cur_chr == 0) + mval = scan_delimiter_as_mathchar(tex_mathcode); + else if (cur_chr == 1) + mval = scan_delimiter_as_mathchar(umath_mathcode); + else + confusion("scan_math"); + break; + default: + /* The pointer |p| is placed on |save_stack| while a complex subformula + is being scanned. */ + back_input(); + scan_left_brace(); + set_saved_record(0, saved_math, 0, p); + incr(save_ptr); + push_math(math_group, mstyle); + return 1; + } + type(p) = math_char_node; + math_character(p) = mval.character_value; + if ((mval.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(p) = cur_fam_par; + else + math_fam(p) = mval.family_value; + return 0; +} + +@ The |set_math_char| procedure creates a new noad appropriate to a given +math code, and appends it to the current mlist. However, if the math code +is sufficiently large, the |cur_chr| is treated as an active character and +nothing is appended. + +@c +void set_math_char(mathcodeval mval) +{ + pointer p; /* the new noad */ + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + pointer q; + p = new_noad(); + q = new_node(math_char_node, 0); + nucleus(p) = q; + math_character(nucleus(p)) = mval.character_value; + math_fam(nucleus(p)) = mval.family_value; + if (mval.class_value == math_use_current_family_code) { + if (cur_fam_par_in_range) + math_fam(nucleus(p)) = cur_fam_par; + subtype(p) = ord_noad_type; + } else { + switch (mval.class_value) { + /* *INDENT-OFF* */ + case 0: subtype(p) = ord_noad_type; break; + case 1: subtype(p) = op_noad_type_normal; break; + case 2: subtype(p) = bin_noad_type; break; + case 3: subtype(p) = rel_noad_type; break; + case 4: subtype(p) = open_noad_type; break; + case 5: subtype(p) = close_noad_type; break; + case 6: subtype(p) = punct_noad_type; break; + /* *INDENT-ON* */ + } + } + vlink(tail) = p; + tail = p; + } +} + +@ The |math_char_in_text| procedure creates a new node representing a math char +in text code, and appends it to the current list. However, if the math code +is sufficiently large, the |cur_chr| is treated as an active character and +nothing is appended. + +@c +void math_char_in_text(mathcodeval mval) +{ + pointer p; /* the new node */ + if (mval.class_value == 8) { + /* An active character that is an |outer_call| is allowed here */ + cur_cs = active_to_cs(cur_chr, true); + cur_cmd = eq_type(cur_cs); + cur_chr = equiv(cur_cs); + x_token(); + back_input(); + } else { + p = new_char(fam_fnt(mval.family_value, text_size), mval.character_value); + vlink(tail) = p; + tail = p; + } +} + +@ @c +void math_math_comp(void) +{ + pointer q; + tail_append(new_noad()); + subtype(tail) = (quarterword) cur_chr; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + if (cur_chr == over_noad_type) + (void) scan_math(nucleus(tail), cramped_style(m_style)); + else + (void) scan_math(nucleus(tail), m_style); +} + +@ @c +void math_limit_switch(void) +{ + const char *hlp[] = { + "I'm ignoring this misplaced \\limits or \\nolimits command.", + NULL + }; + if (head != tail) { + if (type(tail) == simple_noad && + (subtype(tail) == op_noad_type_normal || + subtype(tail) == op_noad_type_limits || + subtype(tail) == op_noad_type_no_limits)) { + subtype(tail) = (quarterword) cur_chr; + return; + } + } + tex_error("Limit controls must follow a math operator", hlp); +} + +@ Delimiter fields of noads are filled in by the |scan_delimiter| routine. +The first parameter of this procedure is the |mem| address where the +delimiter is to be placed; the second tells if this delimiter follows +\.{\\radical} or not. + +@c +static void scan_delimiter(pointer p, int r) +{ + delcodeval dval = { 0, 0, 0, 0, 0 }; + if (r == tex_mathcode) { /* \.{\\radical} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + } else if (r == umath_mathcode) { /* \.{\\Uradical} */ + dval = do_scan_extdef_del_code(umath_mathcode, false); + } else if (r == no_mathcode) { + get_next_nb_nr(); + switch (cur_cmd) { + case letter_cmd: + case other_char_cmd: + dval = get_del_code(cur_chr); + break; + case delim_num_cmd: + if (cur_chr == 0) /* \.{\\delimiter} */ + dval = do_scan_extdef_del_code(tex_mathcode, true); + else if (cur_chr == 1) /* \.{\\Udelimiter} */ + dval = do_scan_extdef_del_code(umath_mathcode, true); + else + confusion("scan_delimiter1"); + break; + default: + dval.small_family_value = -1; + break; + } + } else { + confusion("scan_delimiter2"); + } + if (p == null) + return; + if (dval.small_family_value < 0) { + const char *hlp[] = { + "I was expecting to see something like `(' or `\\{' or", + "`\\}' here. If you typed, e.g., `{' instead of `\\{', you", + "should probably delete the `{' by typing `1' now, so that", + "braces don't get unbalanced. Otherwise just proceed", + "Acceptable delimiters are characters whose \\delcode is", + "nonnegative, or you can use `\\delimiter '.", + NULL + }; + back_error("Missing delimiter (. inserted)", hlp); + small_fam(p) = 0; + small_char(p) = 0; + large_fam(p) = 0; + large_char(p) = 0; + } else { + small_fam(p) = dval.small_family_value; + small_char(p) = dval.small_character_value; + large_fam(p) = dval.large_family_value; + large_char(p) = dval.large_character_value; + } + return; +} + +@ @c +void math_radical(void) +{ + halfword q; + int chr_code = cur_chr; + halfword options = 0; + tail_append(new_node(radical_noad, chr_code)); + q = new_node(delim_node, 0); + left_delimiter(tail) = q; + while (1) { + if (scan_keyword("width")) { + scan_dimen(false,false,false); + radicalwidth(tail) = cur_val ; + } else if (scan_keyword("left")) { + options = options | noad_option_left ; + } else if (scan_keyword("middle")) { + options = options | noad_option_middle ; + } else if (scan_keyword("right")) { + options = options | noad_option_right ; + } else { + break; + } + } + radicaloptions(tail) = options; + if (chr_code == 0) /* \.{\\radical} */ + scan_delimiter(left_delimiter(tail), tex_mathcode); + else if (chr_code == 1) /* \.{\\Uradical} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 2) /* \.{\\Uroot} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 3) /* \.{\\Uunderdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 4) /* \.{\\Uoverdelimiter} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 5) /* \.{\\Udelimiterunder} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 6) /* \.{\\Udelimiterover} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else if (chr_code == 7) /* \.{\\Uhextensible} */ + scan_delimiter(left_delimiter(tail), umath_mathcode); + else + confusion("math_radical"); + if (chr_code == 7) { + q = new_node(sub_box_node, 0); /* type will change */ + nucleus(tail) = q; + return; + } else if (chr_code == 2) { + /* the trick with the |vlink(q)| is used by |scan_math| + to decide whether it needs to go on */ + q = new_node(math_char_node, 0); + vlink(q) = tail; + degree(tail) = q; + if (!scan_math(degree(tail), sup_sup_style(m_style))) { + vlink(degree(tail)) = null; + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } + } else { + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); + } +} + +@ @c +void math_ac(void) +{ + halfword q; + mathcodeval t = { 0, 0, 0 }; + mathcodeval b = { 0, 0, 0 }; + mathcodeval o = { 0, 0, 0 }; + if (cur_cmd == accent_cmd) { + const char *hlp[] = { + "I'm changing \\accent to \\mathaccent here; wish me luck.", + "(Accents are not the same in formulas as they are in text.)", + NULL + }; + tex_error("Please use \\mathaccent for accents in math mode", hlp); + } + tail_append(new_node(accent_noad, 0)); + if (cur_chr == 0) { /* \.{\\mathaccent} */ + t = scan_mathchar(tex_mathcode); + } else if (cur_chr == 1) { /* \.{\\Umathaccent} */ + if (scan_keyword("fixed")) { + /* top */ + subtype(tail) = 1; + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("both")) { + /* top bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + t = scan_mathchar(umath_mathcode); + if (scan_keyword("fixed")) { + subtype(tail) += 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("bottom")) { + /* bottom */ + if (scan_keyword("fixed")) { + subtype(tail) = 2; + } + b = scan_mathchar(umath_mathcode); + } else if (scan_keyword("top")) { + /* top */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + t = scan_mathchar(umath_mathcode); + } else if (scan_keyword("overlay")) { + /* overlay */ + if (scan_keyword("fixed")) { + subtype(tail) = 1; + } + o = scan_mathchar(umath_mathcode); + } else { + /* top */ + t = scan_mathchar(umath_mathcode); + } + if (scan_keyword("fraction")) { + scan_int(); + accentfraction(tail) = cur_val; + } + } else { + confusion("mathaccent"); + } + if (!(t.character_value == 0 && t.family_value == 0)) { + q = new_node(math_char_node, 0); + top_accent_chr(tail) = q; + math_character(top_accent_chr(tail)) = t.character_value; + if ((t.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(top_accent_chr(tail)) = cur_fam_par; + else + math_fam(top_accent_chr(tail)) = t.family_value; + } + if (!(b.character_value == 0 && b.family_value == 0)) { + q = new_node(math_char_node, 0); + bot_accent_chr(tail) = q; + math_character(bot_accent_chr(tail)) = b.character_value; + if ((b.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(bot_accent_chr(tail)) = cur_fam_par; + else + math_fam(bot_accent_chr(tail)) = b.family_value; + } + if (!(o.character_value == 0 && o.family_value == 0)) { + q = new_node(math_char_node, 0); + overlay_accent_chr(tail) = q; + math_character(overlay_accent_chr(tail)) = o.character_value; + if ((o.class_value == math_use_current_family_code) && cur_fam_par_in_range) + math_fam(overlay_accent_chr(tail)) = cur_fam_par; + else + math_fam(overlay_accent_chr(tail)) = o.family_value; + } + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math(nucleus(tail), cramped_style(m_style)); +} + +@ @c +pointer math_vcenter_group(pointer p) +{ + pointer q, r; + q = new_noad(); + subtype(q) = vcenter_noad_type; + r = new_node(sub_box_node, 0); + nucleus(q) = r; + math_list(nucleus(q)) = p; + return q; +} + +@ The routine that scans the four mlists of a \.{\\mathchoice} is very +much like the routine that builds discretionary nodes. + +@c +void append_choices(void) +{ + tail_append(new_choice()); + incr(save_ptr); + set_saved_record(-1, saved_choices, 0, 0); + push_math(math_choice_group, display_style); + scan_left_brace(); +} + +@ @c +void build_choices(void) +{ + pointer p; /* the current mlist */ + int prev_style; + prev_style = m_style; + unsave_math(); + p = fin_mlist(null); + assert(saved_type(-1) == saved_choices); + switch (saved_value(-1)) { + case 0: + display_mlist(tail) = p; + break; + case 1: + text_mlist(tail) = p; + break; + case 2: + script_mlist(tail) = p; + break; + case 3: + script_script_mlist(tail) = p; + decr(save_ptr); + return; + break; + } /* there are no other cases */ + set_saved_record(-1, saved_choices, 0, (saved_value(-1) + 1)); + push_math(math_choice_group, (prev_style + 2)); + scan_left_brace(); +} + +@ Subscripts and superscripts are attached to the previous nucleus by the +action procedure called |sub_sup|. + +@c +void sub_sup(void) +{ + pointer q; + if (tail == head || (!scripts_allowed(tail))) { + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + } + if (cur_cmd == sup_mark_cmd || cur_chr == sup_mark_cmd) { /* |super_sub_script| */ + if (supscr(tail) != null) { + const char *hlp[] = { + "I treat `x^1^2' essentially like `x^1{}^2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double superscript", hlp); + } + q = new_node(math_char_node, 0); + supscr(tail) = q; + (void) scan_math(supscr(tail), sup_style(m_style)); + } else if (cur_cmd == sub_mark_cmd || cur_chr == sub_mark_cmd) { + if (subscr(tail) != null) { + const char *hlp[] = { + "I treat `x_1_2' essentially like `x_1{}_2'.", NULL + }; + tail_append(new_noad()); + q = new_node(sub_mlist_node, 0); + nucleus(tail) = q; + tex_error("Double subscript", hlp); + } + q = new_node(math_char_node, 0); + subscr(tail) = q; + (void) scan_math(subscr(tail), sub_style(m_style)); + } +} + +@ An operation like `\.{\\over}' causes the current mlist to go into a +state of suspended animation: |incompleat_noad| points to a |fraction_noad| +that contains the mlist-so-far as its numerator, while the denominator +is yet to come. Finally when the mlist is finished, the denominator will +go into the incompleat fraction noad, and that noad will become the +whole formula, unless it is surrounded by `\.{\\left}' and `\.{\\right}' +delimiters. + +@c +void math_fraction(void) +{ + halfword c; /* the type of generalized fraction we are scanning */ + pointer q; + halfword options = 0; + halfword temp_value; + c = cur_chr; + if (incompleat_noad_par != null) { + const char *hlp[] = { + "I'm ignoring this fraction specification, since I don't", + "know whether a construction like `x \\over y \\over z'", + "means `{x \\over y} \\over z' or `x \\over {y \\over z}'.", + NULL + }; + if (c >= delimited_code) { + scan_delimiter(null, no_mathcode); + scan_delimiter(null, no_mathcode); + } + if ((c % delimited_code) == above_code) + scan_normal_dimen(); + tex_error("Ambiguous; you need another { and }", hlp); + } else { + incompleat_noad_par = new_node(fraction_noad, 0); + temp_value = new_node(sub_mlist_node, 0); + numerator(incompleat_noad_par) = temp_value; + math_list(numerator(incompleat_noad_par)) = vlink(head); + vlink(head) = null; + tail = head; + m_style = cramped_style(m_style); + + if ((c % delimited_code) == skewed_code) { + q = new_node(delim_node, 0); + middle_delimiter(incompleat_noad_par) = q; + scan_delimiter(middle_delimiter(incompleat_noad_par), no_mathcode); + } + if (c >= delimited_code) { + q = new_node(delim_node, 0); + left_delimiter(incompleat_noad_par) = q; + q = new_node(delim_node, 0); + right_delimiter(incompleat_noad_par) = q; + scan_delimiter(left_delimiter(incompleat_noad_par), no_mathcode); + scan_delimiter(right_delimiter(incompleat_noad_par), no_mathcode); + } + switch (c % delimited_code) { + case above_code: + while (1) { + if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else { + break; + } + } + fractionoptions(incompleat_noad_par) = options; + scan_normal_dimen(); + thickness(incompleat_noad_par) = cur_val; + break; + case over_code: + thickness(incompleat_noad_par) = default_code; + break; + case atop_code: + thickness(incompleat_noad_par) = 0; + break; + case skewed_code: + while (1) { + if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("noaxis")) { + options = options | noad_option_no_axis ; + } else { + break; + } + } + fractionoptions(incompleat_noad_par) = options; + thickness(incompleat_noad_par) = 0; + break; + } + } +} + +@ At the end of a math formula or subformula, the |fin_mlist| routine is +called upon to return a pointer to the newly completed mlist, and to +pop the nest back to the enclosing semantic level. The parameter to +|fin_mlist|, if not null, points to a |fence_noad| that ends the +current mlist; this |fence_noad| has not yet been appended. + +@c +pointer fin_mlist(pointer p) +{ + pointer q; /* the mlist to return */ + if (incompleat_noad_par != null) { + if (denominator(incompleat_noad_par) != null) { + type(denominator(incompleat_noad_par)) = sub_mlist_node; + } else { + q = new_node(sub_mlist_node, 0); + denominator(incompleat_noad_par) = q; + } + math_list(denominator(incompleat_noad_par)) = vlink(head); + if (p == null) { + q = incompleat_noad_par; + } else { + q = math_list(numerator(incompleat_noad_par)); + if ((type(q) != fence_noad) || (subtype(q) != left_noad_side) + || (delim_par == null)) + confusion("right"); /* this can't happen */ + math_list(numerator(incompleat_noad_par)) = vlink(delim_par); + vlink(delim_par) = incompleat_noad_par; + vlink(incompleat_noad_par) = p; + } + } else { + vlink(tail) = p; + q = vlink(head); + } + pop_nest(); + return q; +} + +@ Now at last we're ready to see what happens when a right brace occurs +in a math formula. Two special cases are simplified here: Braces are effectively +removed when they surround a single Ord without sub/superscripts, or when they +surround an accent that is the nucleus of an Ord atom. + +@c +void close_math_group(pointer p) +{ + int old_style = m_style; + unsave_math(); + + decr(save_ptr); + assert(saved_type(0) == saved_math); + type(saved_value(0)) = sub_mlist_node; + p = fin_mlist(null); + math_list(saved_value(0)) = p; + if (p != null) { + if (vlink(p) == null) { + if (type(p) == simple_noad && subtype(p) == ord_noad_type) { + if (subscr(p) == null && supscr(p) == null) { + type(saved_value(0)) = type(nucleus(p)); + if (type(nucleus(p)) == math_char_node) { + math_fam(saved_value(0)) = math_fam(nucleus(p)); + math_character(saved_value(0)) = + math_character(nucleus(p)); + } else { + math_list(saved_value(0)) = math_list(nucleus(p)); + math_list(nucleus(p)) = null; + } + delete_attribute_ref(node_attr(saved_value(0))); + node_attr(saved_value(0)) = node_attr(nucleus(p)); + node_attr(nucleus(p)) = null; + flush_node(p); + } + } else if (type(p) == accent_noad) { + if (saved_value(0) == nucleus(tail)) { + if (type(tail) == simple_noad + && subtype(tail) == ord_noad_type) { + pointer q = head; + while (vlink(q) != tail) + q = vlink(q); + vlink(q) = p; + nucleus(tail) = null; + subscr(tail) = null; + supscr(tail) = null; + delete_attribute_ref(node_attr(p)); + node_attr(p) = node_attr(tail); + node_attr(tail) = null; + flush_node(tail); + tail = p; + } + } + } + } + } + if (vlink(saved_value(0)) > 0) { + pointer q; + q = new_node(math_char_node, 0); + nucleus(vlink(saved_value(0))) = q; + vlink(saved_value(0)) = null; + saved_value(0) = q; + (void) scan_math(saved_value(0), old_style); + /* restart */ + } +} + +@ We have dealt with all constructions of math mode except `\.{\\left}' and +`\.{\\right}', so the picture is completed by the following sections of +the program. The |middle| feature of eTeX allows one ore several \.{\\middle} +delimiters to appear between \.{\\left} and \.{\\right}. + +@c +void math_left_right(void) +{ + halfword t; /* |left_noad_side| .. |right_noad_side| */ + pointer p; /* new noad */ + pointer q; /* resulting mlist */ + pointer r; /* temporary */ + halfword ht = 0; + halfword dp = 0; + halfword options = 0; + halfword type = -1 ; + t = cur_chr; + + if (t > 10) { + /* we have \Uleft \Uright \Umiddle */ + t = t - 10; + while (1) { + if (scan_keyword("height")) { + scan_dimen(false,false,false); + ht = cur_val ; + } else if (scan_keyword("depth")) { + scan_dimen(false,false,false); + dp = cur_val ; + } else if (scan_keyword("axis")) { + options = options | noad_option_axis ; + } else if (scan_keyword("noaxis")) { + options = options | noad_option_no_axis ; + } else if (scan_keyword("exact")) { + options = options | noad_option_exact ; + } else if (scan_keyword("class")) { + scan_int(); + type = cur_val ; + } else { + break; + } + } + } + + if ((t != no_noad_side) && (t != left_noad_side) && (cur_group != math_left_group)) { + if (cur_group == math_shift_group) { + scan_delimiter(null, no_mathcode); + if (t == middle_noad_side) { + const char *hlp[] = { + "I'm ignoring a \\middle that had no matching \\left.", + NULL + }; + tex_error("Extra \\middle", hlp); + } else { + const char *hlp[] = { + "I'm ignoring a \\right that had no matching \\left.", + NULL + }; + tex_error("Extra \\right", hlp); + } + } else { + off_save(); + } + } else { + p = new_noad(); + type(p) = fence_noad; + subtype(p) = (quarterword) t; + r = new_node(delim_node, 0); + delimiter(p) = r; + + delimiterheight(p) = ht; + delimiterdepth(p) = dp; + delimiteroptions(p) = options; + delimiterclass(p) = type; + delimiteritalic(p) = 0; + + scan_delimiter(delimiter(p), no_mathcode); + + if (t == no_noad_side) { + tail_append(new_noad()); + subtype(tail) = inner_noad_type; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = p; + return ; + } + + if (t == left_noad_side) { + q = p; + } else { + q = fin_mlist(p); + unsave_math(); + } + if (t != right_noad_side) { + push_math(math_left_group, m_style); + vlink(head) = q; + tail = p; + delim_par = p; + } else { + tail_append(new_noad()); + subtype(tail) = inner_noad_type; + r = new_node(sub_mlist_node, 0); + nucleus(tail) = r; + math_list(nucleus(tail)) = q; + } + } +} + +@ \TeX\ gets to the following part of the program when +the first `\.\$' ending a display has been scanned. + +@c +static void check_second_math_shift(void) +{ + get_x_token(); + if (cur_cmd != math_shift_cmd) { + const char *hlp[] = { + "The `$' that I just saw supposedly matches a previous `$$'.", + "So I shall assume that you typed `$$' both times.", + NULL + }; + back_error("Display math should end with $$", hlp); + } +} + +static void check_display_math_end(void) +{ + if (cur_chr != cramped_display_style) { + const char *hlp[] = { + "I shall assume that you typed that.", + NULL + }; + tex_error("Display math should end with \\Ustopdisplaymath", hlp); + } +} + +static void check_inline_math_end(void) +{ + if (cur_chr != cramped_text_style) { + const char *hlp[] = { + "I shall assume that you typed that.", + NULL + }; + tex_error("Inline math should end with \\Ustopmath", hlp); + } +} + +@ @c +static void resume_after_display(void) +{ + if (cur_group != math_shift_group) + confusion("display"); + unsave_math(); + prev_graf_par = prev_graf_par + 3; + push_nest(); + mode = hmode; + space_factor_par = 1000; + /* this needs to be intercepted in the display math start ! */ + tail_append(make_local_par_node(penalty_par_code)); + get_x_token(); + if (cur_cmd != spacer_cmd) + back_input(); + if (nest_ptr == 1) { + normal_page_filter(after_display); + build_page(); + } +} + +@ The fussiest part of math mode processing occurs when a displayed formula is +being centered and placed with an optional equation number. + +At this time we are in vertical mode (or internal vertical mode). + + |p| points to the mlist for the formula. + |a| is either |null| or it points to a box containing the equation number. + |l| is true if there was an \.{\\leqno}/ (so |a| is a horizontal box). + +@c +#define inject_display_skip_before(g) \ + switch (display_skip_mode_par) { \ + case 0 : /* normal tex */ \ + tail_append(new_param_glue(g)); \ + break;\ + case 1 : /* always */ \ + tail_append(new_param_glue(g)); \ + break; \ + case 2 : /* non-zero */ \ + if (g != 0 && ! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 3: /* ignore */ \ + break; \ + } + +#define inject_display_skip_after(g) \ + switch (display_skip_mode_par) { \ + case 0 : /* normal tex */ \ + if (g != 0 && glue_is_positive(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 1 : /* always */ \ + tail_append(new_param_glue(g)); \ + break; \ + case 2 : /* non-zero */ \ + if (g != 0 && ! glue_is_zero(glue_par(g))) \ + tail_append(new_param_glue(g)); \ + break; \ + case 3: /* ignore */ \ + break; \ + } + +static void finish_displayed_math(boolean l, pointer eqno_box, pointer p) +{ + pointer eq_box; /* box containing the equation */ + scaled eq_w; /* width of the equation */ + scaled line_w; /* width of the line */ + scaled eqno_w; /* width of equation number */ + scaled eqno_w2; /* width of equation number plus space to separate from equation */ + scaled line_s; /* move the line right this much */ + scaled d; /* displacement of equation in the line */ + small_number g1, g2; /* glue parameter codes for before and after */ + pointer r,s; /* kern nodes used to position the display */ + pointer t; /* tail of adjustment list */ + pointer pre_t; /* tail of pre-adjustment list */ + boolean swap_dir; /* true if the math and surrounding text dirs are opposed */ + scaled eqno_width; + swap_dir = (pre_display_direction_par < 0 ? true : false ); + if (eqno_box != null && swap_dir) + l = !l; + adjust_tail = adjust_head; + pre_adjust_tail = pre_adjust_head; + eq_box = hpack(p, 0, additional, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + p = list_ptr(eq_box); + t = adjust_tail; + adjust_tail = null; + pre_t = pre_adjust_tail; + pre_adjust_tail = null; + eq_w = width(eq_box); + line_w = display_width_par; + line_s = display_indent_par; + if (eqno_box == null) { + eqno_w = 0; + eqno_width = 0; + eqno_w2 = 0; + } else { + eqno_w = width(eqno_box); + eqno_width = eqno_w; + eqno_w2 = eqno_w + round_xn_over_d(math_eqno_gap_step_par, get_math_quad_style(text_style), 1000); + subtype(eqno_box) = equation_number_list; /* new */ + /* build_attribute_list(eqno_box); */ /* probably already set */ + } + if (eq_w + eqno_w2 > line_w) { + /* The user can force the equation number to go on a separate line + by causing its width to be zero. */ + if ((eqno_w != 0) && ((eq_w - total_shrink[normal] + eqno_w2 <= line_w) + || (total_shrink[sfi] != 0) + || (total_shrink[fil] != 0) + || (total_shrink[fill] != 0) + || (total_shrink[filll] != 0))) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w - eqno_w2, exactly, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + } else { + eqno_w = 0; + if (eq_w > line_w) { + list_ptr(eq_box) = null; + flush_node(eq_box); + eq_box = hpack(p, line_w, exactly, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + } + } + eq_w = width(eq_box); + } + /* We try first to center the display without regard to the existence of + the equation number. If that would make it too close (where ``too close'' + means that the space between display and equation number is less than the + width of the equation number), we either center it in the remaining space + or move it as far from the equation number as possible. The latter alternative + is taken only if the display begins with glue, since we assume that the + user put glue there to control the spacing precisely. + */ + d = half(line_w - eq_w); + if ((eqno_w > 0) && (d < 2 * eqno_w)) { /* too close */ + d = half(line_w - eq_w - eqno_w); + if (p != null) + if (!is_char_node(p)) + if (type(p) == glue_node) + d = 0; + } + + tail_append(new_penalty(pre_display_penalty_par,after_display_penalty)); + if ((d + line_s <= pre_display_size_par) || l) { /* not enough clearance */ + g1 = above_display_skip_code; + g2 = below_display_skip_code; + } else { + g1 = above_display_short_skip_code; + g2 = below_display_short_skip_code; + } + + /* If the equation number is set on a line by itself, either before or + after the formula, we append an infinite penalty so that no page break will + separate the display from its number; and we use the same size and + displacement for all three potential lines of the display, even though + `\.{\\parshape}' may specify them differently. + */ + /* \.{\\leqno} on a forced single line due to |width=0| */ + /* it follows that |type(a)=hlist_node| */ + + if (eqno_box && l && (eqno_w == 0)) { + /* if (math_direction_par==dir_TLT) { */ + shift_amount(eqno_box) = 0; + /* } else { */ + /* } */ + append_to_vlist(eqno_box,lua_key_index(equation_number)); + tail_append(new_penalty(inf_penalty,equation_number_penalty)); + } else { + inject_display_skip_before(g1); + } + + if (eqno_w != 0) { + r = new_kern(line_w - eq_w - eqno_w - d); + if (l) { + if (swap_dir) { + if (math_direction_par==dir_TLT) { + /* TRT + TLT + \eqno, (swap_dir=true, math_direction_par=TLT, l=true) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 1\n"); +#endif + s = new_kern(width(r) + eqno_w); + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + try_couple_nodes(eq_box,s); + } else { + /* TLT + TRT + \eqno, (swap_dir=true, math_direction_par=TRT, l=true) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 2\n"); +#endif + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + } + } else { + if (math_direction_par==dir_TLT) { + /* TLT + TLT + \leqno, (swap_dir=false, math_direction_par=TLT, l=true) */ /* OK */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 3\n"); +#endif + s = new_kern(width(r) + eqno_w); + } else { + /* TRT + TRT + \leqno, (swap_dir=false, math_direction_par=TRT, l=true) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 4\n"); +#endif + s = new_kern(width(r)); + } + try_couple_nodes(eqno_box,r); + try_couple_nodes(r,eq_box); + try_couple_nodes(eq_box,s); + } + eq_box = eqno_box; + } else { + if (swap_dir) { + if (math_direction_par==dir_TLT) { + /* TRT + TLT + \leqno, (swap_dir=true, math_direction_par=TLT, l=false) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 5\n"); +#endif + } else { + /* TLT + TRT + \leqno, (swap_dir=true, math_direction_par=TRT, l=false) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 6\n"); +#endif + } + try_couple_nodes(eq_box,r); + try_couple_nodes(r,eqno_box); + } else { + if (math_direction_par==dir_TLT) { + /* TLT + TLT + \eqno, (swap_dir=false, math_direction_par=TLT, l=false) */ /* OK */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 7\n"); +#endif + s = new_kern(d); + } else { + /* TRT + TRT + \eqno, (swap_dir=false, math_direction_par=TRT, l=false) */ +#ifdef DEBUG + fprintf(stderr, "\nDEBUG: CASE 8\n"); +#endif + s = new_kern(width(r) + eqno_w); + } + try_couple_nodes(s,eq_box); + try_couple_nodes(eq_box,r); + try_couple_nodes(r,eqno_box); + eq_box = s; + } + } + eq_box = hpack(eq_box, 0, additional, -1); + subtype(eq_box) = equation_list; /* new */ + build_attribute_list(eq_box); + shift_amount(eq_box) = line_s; + } else { + shift_amount(eq_box) = line_s + d; + } +/* check for prev: */ + append_to_vlist(eq_box,lua_key_index(equation)); + + if ((eqno_box != null) && (eqno_w == 0) && !l) { + tail_append(new_penalty(inf_penalty,equation_number_penalty)); + /* if (math_direction_par==dir_TLT) { */ + shift_amount(eqno_box) = line_s + line_w - eqno_width ; + /* } else { */ + /* } */ + append_to_vlist(eqno_box,lua_key_index(equation_number)); + g2 = 0; + } + if (t != adjust_head) { /* migrating material comes after equation number */ + vlink(tail) = vlink(adjust_head); + /* needs testing */ + alink(adjust_tail) = alink(tail); + tail = t; + } + if (pre_t != pre_adjust_head) { + vlink(tail) = vlink(pre_adjust_head); + /* needs testing */ + alink(pre_adjust_tail) = alink(tail); + tail = pre_t; + } + tail_append(new_penalty(post_display_penalty_par,after_display_penalty)); + inject_display_skip_after(g2); + resume_after_display(); +} + +@ @c +void after_math(void) +{ + int m; /* |mmode| or |-mmode| */ + pointer p; /* the formula */ + pointer a = null; /* box containing equation number */ + boolean l = false; /* `\.{\\leqno}' instead of `\.{\\eqno}' */ + m = mode; + p = fin_mlist(null); /* this pops the nest */ + if (cur_cmd == math_shift_cs_cmd && + (cur_chr == text_style || cur_chr == display_style)) { + you_cant(); + } + if (mode == -m) { /* end of equation number */ + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + run_mlist_to_hlist(p, false, text_style); + a = hpack(vlink(temp_head), 0, additional, -1); + build_attribute_list(a); + unsave_math(); + decr(save_ptr); /* now |cur_group=math_shift_group| */ + assert(saved_type(0) == saved_eqno); + if (saved_value(0) == 1) + l = true; + m = mode; + p = fin_mlist(null); + + } + if (m < 0) { + /* The |unsave| is done after everything else here; hence an appearance of + `\.{\\mathsurround}' inside of `\.{\$...\$}' affects the spacing at these + particular \.\$'s. This is consistent with the conventions of + `\.{\$\$...\$\$}', since `\.{\\abovedisplayskip}' inside a display affects the + space above that display. + */ + if (cur_cmd == math_shift_cs_cmd) { + check_inline_math_end(); + } + tail_append(new_math(math_surround_par, before)); + /* begin mathskip code */ + switch (math_skip_mode) { + case 0 : + /* obey mathsurround when zero glue */ + if (! glue_is_zero(math_skip_par)) { + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + } + break ; + case 1 : + /* always left */ + case 3 : + /* always both */ + case 6 : + /* only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 2 : + /* only right */ + surround(tail) = 0; + break ; + case 4 : + /* ignore, obey marthsurround */ + break ; + case 5: + /* all spacing disabled */ + surround(tail) = 0; + break ; + } + /* end mathskip code */ + if (dir_math_save) { + tail_append(new_dir(math_direction_par)); + } + run_mlist_to_hlist(p, (mode > 0), text_style); + vlink(tail) = vlink(temp_head); + while (vlink(tail) != null) { + tail = vlink(tail); + } + if (dir_math_save) { + tail_append(new_dir(math_direction_par - dir_swap)); + } + dir_math_save = false; + tail_append(new_math(math_surround_par, after)); + /* begin mathskip code */ + switch (math_skip_mode) { + case 0 : + /* obey mathsurround when zero glue */ + if (! glue_is_zero(math_skip_par)) { + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + } + break ; + case 2 : + /* always right */ + case 3 : + /* always both */ + case 6 : + /* only when skip */ + copy_glue_values(tail,math_skip_par); + surround(tail) = 0; + break ; + case 1 : + /* only left */ + surround(tail) = 0; + break ; + case 4 : + /* ignore, obey marthsurround */ + break ; + case 5: + /* all spacing disabled */ + surround(tail) = 0; + break ; + } + /* end mathskip code */ + space_factor_par = 1000; + unsave_math(); + } else { + if (a == null) { + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + } + run_mlist_to_hlist(p, false, display_style); + finish_displayed_math(l, a, vlink(temp_head)); + } +} + +@ When \.{\\halign} appears in a display, the alignment routines operate +essentially as they do in vertical mode. Then the following program is +activated, with |p| and |q| pointing to the beginning and end of the +resulting list, and with |aux_save| holding the |prev_depth| value. + +@c +void finish_display_alignment(pointer p, pointer q, halfword saved_prevdepth) +{ + do_assignments(); + if (cur_cmd == math_shift_cmd) { + check_second_math_shift(); + } else { + check_display_math_end(); + } + pop_nest(); + tail_append(new_penalty(pre_display_penalty_par,before_display_penalty)); + inject_display_skip_before(above_display_skip_code); + vlink(tail) = p; + if (p != null) + tail = q; + tail_append(new_penalty(post_display_penalty_par,after_display_penalty)); + inject_display_skip_after(below_display_skip_code); + cur_list.prev_depth_field = saved_prevdepth; + resume_after_display(); +} + +@ Interface to \.{\\Umath} and \.{\\mathstyle} + +@c +void setup_math_style(void) +{ + pointer q; + tail_append(new_noad()); + q = new_node(math_char_node, 0); + nucleus(tail) = q; + (void) scan_math_style(nucleus(tail), num_style(m_style)); +} + +@ @c +void print_math_style(void) +{ + if (abs(mode) == mmode) + print_int(m_style); + else + print_int(-1); +} diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.h b/Build/source/texk/web2c/luatexdir/tex/texnodes.h index dda0925609e..b838dd0a2f9 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.h +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.h @@ -127,6 +127,18 @@ typedef enum { # define penalty_node_size 3 # define penalty(a) vlink((a)+2) +typedef enum { + user_penalty, + linebreak_penalty, /* includes widow, club, broken ect */ + line_penalty, + word_penalty, + final_penalty, + noad_penalty, + before_display_penalty, + after_display_penalty, + equation_number_penalty, +} penalty_subtypes ; + /* # define glue_node_size 4 # define glue_ptr(a) vinfo((a)+2) @@ -993,7 +1005,7 @@ extern halfword new_param_glue(int n); extern halfword new_glue(halfword q); extern halfword new_skip_param(int n); extern halfword new_kern(scaled w); -extern halfword new_penalty(int m); +extern halfword new_penalty(int m, int s); extern int lua_properties_enabled ; extern int lua_properties_level ; diff --git a/Build/source/texk/web2c/luatexdir/tex/texnodes.w b/Build/source/texk/web2c/luatexdir/tex/texnodes.w index 9ba54f07432..7967006ec35 100644 --- a/Build/source/texk/web2c/luatexdir/tex/texnodes.w +++ b/Build/source/texk/web2c/luatexdir/tex/texnodes.w @@ -1,3775 +1,3777 @@ -% texnodes.w -% -% Copyright 2006-2010 Taco Hoekwater -% -% This file is part of LuaTeX. -% -% LuaTeX is free software; you can redistribute it and/or modify it under -% the terms of the GNU General Public License as published by the Free -% Software Foundation; either version 2 of the License, or (at your -% option) any later version. -% -% LuaTeX is distributed in the hope that it will be useful, but WITHOUT -% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public -% License for more details. -% -% You should have received a copy of the GNU General Public License along -% with LuaTeX; if not, see . - -@ @c - -#include "ptexlib.h" -#include "lua/luatex-api.h" - -/* we can consider less mode sizes: 2 4 6 8 */ - -@ -This module started out using NDEBUG to trigger checking invalid node usage, -something that is needed because users can mess up nodes in Lua. At some point -that code was always enabled so it is now always on but still can be recognized -as additional code. And as the performance hit is close to zero so disabling -makes no sense, not even to make it configureable. There is a little more memory -used but that is neglectable compared to other memory usage. - -@c -#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ -#define CHECK_NODE_USAGE 1 /* this triggers checking */ - -memory_word *volatile varmem = NULL; - -#ifdef CHECK_NODE_USAGE - char *varmem_sizes = NULL; -#endif - -halfword var_mem_max = 0; -halfword rover = 0; - -halfword free_chain[MAX_CHAIN_SIZE] = { null }; - -static int my_prealloc = 0; - -int fix_node_lists = 1; /* used in font and lang */ - -halfword slow_get_node(int s); /* defined below */ - -#define fake_node 100 -#define fake_node_size 2 -#define fake_node_name "fake" - -#define variable_node_size 2 - -/* core nodes */ - -const char *node_fields_list[] = { - "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign", - "glue_set", "head", NULL -}; -const char *node_fields_rule[] = { - "attr", "width", "depth", "height", "dir", "index", NULL -}; -const char *node_fields_insert[] = { - "attr", "cost", "depth", "height", "spec", "head", NULL -}; -const char *node_fields_mark[] = { - "attr", "class", "mark", NULL -}; -const char *node_fields_adjust[] = { - "attr", "head", NULL -}; -const char *node_fields_disc[] = { - "attr", "pre", "post", "replace", "penalty", NULL -}; -const char *node_fields_math[] = { - "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_glue[] = { - "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_kern[] = { - "attr", "kern", "expansion_factor", NULL -}; -const char *node_fields_penalty[] = { - "attr", "penalty", NULL -}; -const char *node_fields_unset[] = { - "attr", "width", "depth", "height", "dir", "shrink", "glue_order", - "glue_sign", "stretch", "span", "head", NULL -}; -const char *node_fields_margin_kern[] = { - "attr", "width", "glyph", NULL -}; -const char *node_fields_glyph[] = { - "attr", "char", "font", "lang", "left", "right", "uchyph", "components", - "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL -}; -const char *node_fields_inserting[] = { - "height", "last_ins_ptr", "best_ins_ptr", - "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_splitup[] = { - "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL -}; -const char *node_fields_attribute[] = { - "number", "value", NULL -}; -const char *node_fields_glue_spec[] = { - "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL -}; -const char *node_fields_attribute_list[] = { - NULL -}; -const char *node_fields_local_par[] = { - "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width", - "box_right", "box_right_width", NULL -}; -const char *node_fields_dir[] = { - "attr", "dir", "level", NULL -}; -const char *node_fields_boundary[] = { - "attr", "value", NULL -}; - -/* math nodes */ - -const char *node_fields_noad[] = { - "attr", "nucleus", "sub", "sup", NULL -}; - -const char *node_fields_style[] = { - "attr", "style", NULL -}; -const char *node_fields_choice[] = { - "attr", "display", "text", "script", "scriptscript", NULL -}; -const char *node_fields_radical[] = { - "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL -}; -const char *node_fields_fraction[] = { - "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL -}; -const char *node_fields_accent[] = { - "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent", - "overlay_accent", "fraction", NULL -}; -const char *node_fields_fence[] = { - "attr", "delim", "italic", "height", "depth", "options", "class", NULL -}; -const char *node_fields_math_char[] = { - "attr", "fam", "char", NULL -}; -const char *node_fields_sub_box[] = { - "attr", "head", NULL -}; -const char *node_fields_sub_mlist[] = { - "attr", "head", NULL -}; -const char *node_fields_math_text_char[] = { - "attr", "fam", "char", NULL -}; -const char *node_fields_delim[] = { - "attr", "small_fam", "small_char", "large_fam", "large_char", NULL -}; - -/* whatsit nodes */ - -const char *node_fields_whatsit_open[] = { - "attr", "stream", "name", "area", "ext", NULL -}; -const char *node_fields_whatsit_write[] = { - "attr", "stream", "data", NULL -}; -const char *node_fields_whatsit_close[] = { - "attr", "stream", NULL -}; -const char *node_fields_whatsit_special[] = { - "attr", "data", NULL -}; -const char *node_fields_whatsit_save_pos[] = { - "attr", NULL -}; -const char *node_fields_whatsit_late_lua[] = { - "attr", "reg", "data", "name", "string", NULL -}; -const char *node_fields_whatsit_user_defined[] = { - "attr", "user_id", "type", "value", NULL -}; - -/* pdf backend whatsit nodes */ - -const char *node_fields_whatsit_pdf_literal[] = { - "attr", "mode", "data", NULL -}; -const char *node_fields_whatsit_pdf_refobj[] = { - "attr", "objnum", NULL -}; -const char *node_fields_whatsit_pdf_annot[] = { - "attr", "width", "depth", "height", "objnum", "data", NULL -}; -const char *node_fields_whatsit_pdf_start_link[] = { - "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL -}; -const char *node_fields_whatsit_pdf_end_link[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_dest[] = { - "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type", - "xyz_zoom", "objnum", NULL -}; -const char *node_fields_whatsit_pdf_action[] = { - "action_type", "named_id", "action_id", "file", "new_window", "data", NULL -}; -const char *node_fields_whatsit_pdf_thread[] = { - "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL -}; -const char *node_fields_whatsit_pdf_start_thread[] = { - "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL -}; -const char *node_fields_whatsit_pdf_end_thread[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_colorstack[] = { - "attr", "stack", "cmd", "data", NULL -}; -const char *node_fields_whatsit_pdf_setmatrix[] = { - "attr", "data", NULL -}; -const char *node_fields_whatsit_pdf_save[] = { - "attr", NULL -}; -const char *node_fields_whatsit_pdf_restore[] = { - "attr", NULL -}; - -/* subtypes */ - -const char *node_subtypes_glue[] = { - "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip", - "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip", - "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", - "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL -}; -const char *node_subtypes_mathglue[] = { /* 98+ */ - "conditionalmathskip", "muglue", NULL -}; -const char *node_subtypes_leader[] = { /* 100+ */ - "leaders", "cleaders", "xleaders", "gleaders", NULL -}; -const char *node_subtypes_fill[] = { - "stretch", "fi", "fil", "fill", "filll", NULL -}; -const char *node_subtypes_boundary[] = { - "cancel", "user", "protrusion", "word", NULL -}; -const char *node_subtypes_penalty[] = { - "userpenalty", NULL -}; -const char *node_subtypes_kern[] = { - "fontkern", "userkern", "accentkern", "italiccorrection", NULL -}; -const char *node_subtypes_rule[] = { - "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL -}; -const char *node_subtypes_glyph[] = { - "character", "glyph", "ligature", "ghost", "left", "right", NULL -}; -const char *node_subtypes_disc[] = { - "discretionary", "explicit", "automatic", "regular", "first", "second", NULL -}; -const char *node_subtypes_marginkern[] = { - "left", "right", NULL -}; -const char *node_subtypes_list[] = { - "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL -}; -const char *node_subtypes_adjust[] = { - "normal", "pre", NULL -}; -const char *node_subtypes_math[] = { - "beginmath", "endmath", NULL -}; -const char *node_subtypes_noad[] = { - "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close", - "punct", "inner", "under", "over", "vcenter", NULL -}; -const char *node_subtypes_radical[] = { - "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder", - "udelimiterover", NULL -}; -const char *node_subtypes_accent[] = { - "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL, -}; -const char *node_subtypes_fence[] = { - "unset", "left", "middle", "right", NULL -}; - -node_info node_data[] = { /* the last entry in a row is the etex number */ - { hlist_node, box_node_size, node_fields_list, "hlist", 1 }, - { vlist_node, box_node_size, node_fields_list, "vlist", 2 }, - { rule_node, rule_node_size, node_fields_rule, "rule", 3 }, - { ins_node, ins_node_size, node_fields_insert, "ins", 4 }, - { mark_node, mark_node_size, node_fields_mark, "mark", 5 }, - { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 }, - { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 }, - { disc_node, disc_node_size, node_fields_disc, "disc", 8 }, - { whatsit_node, -1, NULL, "whatsit", 9 }, - { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 }, - { dir_node, dir_node_size, node_fields_dir, "dir", -1 }, - { math_node, math_node_size, node_fields_math, "math", 10 }, - { glue_node, glue_node_size, node_fields_glue, "glue", 11 }, - { kern_node, kern_node_size, node_fields_kern, "kern", 12 }, - { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 }, - { unset_node, box_node_size, node_fields_unset, "unset", 14 }, - { style_node, style_node_size, node_fields_style, "style", 15 }, - { choice_node, style_node_size, node_fields_choice, "choice", 15 }, - { simple_noad, noad_size, node_fields_noad, "noad", 15 }, - { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 }, - { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 }, - { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 }, - { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 }, - { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 }, - { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 }, - { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 }, - { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 }, - { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 }, - { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 }, - { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 }, - { align_record_node, box_node_size, NULL, "align_record", -1 }, - { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 }, - { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 }, - { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 }, - { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 }, - { expr_node, expr_node_size, NULL, "expr_stack", -1 }, - { nesting_node, nesting_node_size, NULL, "nested_list", -1 }, - { span_node, span_node_size, NULL, "span", -1 }, - { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 }, - { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 }, - { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 }, - { temp_node, temp_node_size, NULL, "temp", -1 }, - { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 }, - { movement_node, movement_node_size, NULL, "movement_stack", -1 }, - { if_node, if_node_size, NULL, "if_stack", -1 }, - { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 }, - { hyphenated_node, active_node_size, NULL, "hyphenated", -1 }, - { delta_node, delta_node_size, NULL, "delta", -1 }, - { passive_node, passive_node_size, NULL, "passive", -1 }, - { shape_node, variable_node_size, NULL, "shape", -1 }, - { -1, -1, NULL, NULL, -1 }, -}; - -const char *node_subtypes_pdf_destination[] = { - "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL -}; -const char *node_subtypes_pdf_literal[] = { - "origin", "page", "direct", NULL -}; - -node_info whatsit_node_data[] = { - { open_node, open_node_size, node_fields_whatsit_open, "open", -1 }, - { write_node, write_node_size, node_fields_whatsit_write, "write", -1 }, - { close_node, close_node_size, node_fields_whatsit_close, "close", -1 }, - { special_node, special_node_size, node_fields_whatsit_special, "special", -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 }, - { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 }, - { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - { fake_node, fake_node_size, NULL, fake_node_name, -1 }, - /* here starts the dvi backend section, todo: a separate list */ - /* nothing for dvi */ - /* here starts the pdf backend section, todo: a separate list */ - { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 }, - { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 }, - { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 }, - { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 }, - { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 }, - { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 }, - { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 }, - { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 }, - { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 }, - { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 }, - { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 }, - { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 }, - { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 }, - { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 }, - { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 }, - { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 }, - /* done */ - { -1, -1, NULL, NULL, -1 }, -}; - -#define last_whatsit_node pdf_restore_node - -@ -When we copy a node list, there are several possibilities: we do the same as a new node, -we copy the entry to the table in properties (a reference), we do a deep copy of a table -in the properties, we create a new table and give it the original one as a metatable. -After some experiments (that also included timing) with these scenarios I decided that a -deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable -variant were both ok, although the second ons is slower. The most important aspect to keep -in mind is that references to other nodes in properties no longer can be valid for that -copy. We could use two tables (one unique and one shared) or metatables but that only -complicates matters. - -When defining a new node, we could already allocate a table but it is rather easy to do -that at the lua end e.g. using a metatable __index method. That way it is under macro -package control. - -When deleting a node, we could keep the slot (e.g. setting it to false) but it could make -memory consumption raise unneeded when we have temporary large node lists and after that -only small lists. - -So, in the end this is what we ended up with. For the record, I also experimented with the -following: - -- copy attributes to the properties so that we have fast access at the lua end: in the end - the overhead is not compensated by speed and convenience, in fact, attributes are not - that slow when it comes to accessing them - -- a bitset in the node but again the gain compared to attributes is neglectable and it also - demands a pretty string agreement over what bit represents what, and this is unlikely to - succeed in the tex community (I could use it for font handling, which is cross package, - but decided that it doesn't pay off - -In case one wonders why properties make sense then, well, it is not so much speed that we -gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and -this mechanism makes sure that properties are cleaned up when a node is freed. Also, the -advantage of a more or less global properties table is that we stay at the lua end. An -alternative is to store a reference in the node itself but that is complicated by the fact -that the register has some limitations (no numeric keys) and we also don't want to mess with -it too much. - -@c -int lua_properties_level = 0 ; /* can be private */ -int lua_properties_enabled = 0 ; -int lua_properties_use_metatable = 0 ; - -@ -We keep track of nesting so that we don't oveflow the stack, and, what is more -important, don't keep resolving the registry index. - -@c -#define lua_properties_push do { \ - if (lua_properties_enabled) { \ - lua_properties_level = lua_properties_level + 1 ; \ - if (lua_properties_level == 1) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - } \ - } \ -} while(0) - -#define lua_properties_pop do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 1) \ - lua_pop(Luas,1); \ - lua_properties_level = lua_properties_level - 1 ; \ - } \ -} while(0) - -/* No setting is needed: */ - -#define lua_properties_set(target) do { \ -} while(0) - -/* Resetting boils down to nilling. */ - -#define lua_properties_reset(target) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_pushnil(Luas); \ - lua_rawseti(Luas,-2,target); \ - lua_pop(Luas,1); \ - } else { \ - lua_pushnil(Luas); \ - lua_rawseti(Luas,-2,target); \ - } \ - } \ -} while(0) - -/* - For a moment I considered supporting all kind of data types but in practice - that makes no sense. So we stick to a cheap shallow copy with as option a - metatable. Btw, a deep copy would look like this: - - static void copy_lua_table(lua_State* L, int index) { - lua_newtable(L); - lua_pushnil(L); - while(lua_next(L, index-1) != 0) { - lua_pushvalue(L, -2); - lua_insert(L, -2); - if (lua_type(L,-1)==LUA_TTABLE) - copy_lua_table(L,-1); - lua_settable(L, -4); - } - lua_pop(L,1); - } - - #define lua_properties_copy(target, source) do { \ - if (lua_properties_enabled) { \ - lua_pushinteger(Luas,source); \ - lua_rawget(Luas,-2); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - copy_lua_table(Luas,-1); \ - lua_pushinteger(Luas,target); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas,-3); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } while(0) - -*/ - -/* isn't there a faster way to metatable? */ - -/* - -#define lua_properties_copy(target,source) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setfield(Luas,-2,"__index"); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - lua_pop(Luas,1); \ - } else { \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setfield(Luas,-2,"__index"); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } \ -} while(0) - -*/ - -/* - A simple testrun on many pages of dumb text shows 1% gain (of course it depends - on how properties are used but some other tests confirm it). -*/ - -#define lua_properties_copy(target,source) do { \ - if (lua_properties_enabled) { \ - if (lua_properties_level == 0) { \ - lua_get_metatablelua_l(Luas,node_properties); \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_push_string_by_name(Luas,__index); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas, -3); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - lua_pop(Luas,1); \ - } else { \ - lua_rawgeti(Luas,-1,source); \ - if (lua_type(Luas,-1)==LUA_TTABLE) { \ - if (lua_properties_use_metatable) { \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_push_string_by_name(Luas,__index); \ - lua_insert(Luas,-2); \ - lua_rawset(Luas, -3); \ - lua_newtable(Luas); \ - lua_insert(Luas,-2); \ - lua_setmetatable(Luas,-2); \ - } \ - lua_rawseti(Luas,-2,target); \ - } else { \ - lua_pop(Luas,1); \ - } \ - } \ - } \ -} while(0) - -/* Here end the property handlers. */ - -@ @c -int valid_node(halfword p) -{ - if (p > my_prealloc && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - if (varmem_sizes[p] > 0) { - return 1; - } -#else - return 1; -#endif - } - return 0; -} - -@ @c -static int test_count = 1; - -#define dorangetest(a,b,c) do { \ - if (!(b>=0 && b my_prealloc && varmem_sizes[s] == 0) { - s--; - } - if (s != null - && s != my_prealloc - && s != var_mem_max - && (r - s) < get_node_size(type(s), subtype(s)) - && alink(s) != p) { - if (type(s) == disc_node) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s), subtype(s)), (int) s, - (int) vlink(s), (int) alink(s)); - fprintf(stdout, "pre_break(%d,%d,%d), ", - (int) vlink_pre_break(s), (int) tlink(pre_break(s)), - (int) alink(pre_break(s))); - fprintf(stdout, "post_break(%d,%d,%d), ", - (int) vlink_post_break(s), - (int) tlink(post_break(s)), - (int) alink(post_break(s))); - fprintf(stdout, "no_break(%d,%d,%d)", - (int) vlink_no_break(s), (int) tlink(no_break(s)), - (int) alink(no_break(s))); - fprintf(stdout, "\n"); - } else { - if (vlink(s) == p - || (type(s) == glyph_node && lig_ptr (s) == p) - || (type(s) == vlist_node && list_ptr(s) == p) - || (type(s) == hlist_node && list_ptr(s) == p) - || (type(s) == unset_node && list_ptr(s) == p) - || (type(s) == ins_node && ins_ptr (s) == p) - ) { - fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", - get_node_name(type(s), subtype(s)), (int) s, - (int) vlink(s), (int) alink(s)); - if (type(s) == glyph_node) { - fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); - } else if (type(s) == vlist_node || type(s) == hlist_node) { - fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); - } - fprintf(stdout, "\n"); - } else { - if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { - fprintf(stdout, " pointed to from %s node %d\n", - get_node_name(type(s), subtype(s)), (int) s); - } - } - } - } - } - } -} - -#endif - -static int free_error(halfword p) -{ - if (p > my_prealloc && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - int i; - if (varmem_sizes[p] == 0) { - check_static_node_mem(); - for (i = (my_prealloc + 1); i < var_mem_max; i++) { - if (varmem_sizes[i] > 0) { - check_node(i); - } - } - test_count++; - if (type(p) == glyph_node) { - formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); - } else { - formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); - } - node_mem_dump(p); - return 1; - } -#endif - } else { - formatted_error("nodes", "attempt to free an impossible node %d", (int) p); - return 1; - } - return 0; -} - -@ @c -static int copy_error(halfword p) -{ - if (p >= 0 && p < var_mem_max) { -#ifdef CHECK_NODE_USAGE - if (p > my_prealloc && varmem_sizes[p] == 0) { - if (type(p) == glyph_node) { - formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); - } else { - formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); - } - return 1; - } -#endif - } else { - formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); - return 1; - } - return 0; -} - -@ @c -halfword new_node(int i, int j) -{ - int s = get_node_size(i, j); - halfword n = get_node(s); - /* - It should be possible to do this memset at |free_node()|. - - Both type() and subtype() will be set below, and vlink() is - set to null by |get_node()|, so we can do we clearing one - word less than |s| - */ - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1))); - switch (i) { - case glyph_node: - init_lang_data(n); - break; - case hlist_node: - case vlist_node: - box_dir(n) = -1; - break; - case disc_node: - pre_break(n) = pre_break_head(n); - type(pre_break(n)) = nesting_node; - subtype(pre_break(n)) = pre_break_head(0); - post_break(n) = post_break_head(n); - type(post_break(n)) = nesting_node; - subtype(post_break(n)) = post_break_head(0); - no_break(n) = no_break_head(n); - type(no_break(n)) = nesting_node; - subtype(no_break(n)) = no_break_head(0); - break; - case rule_node: - depth(n) = null_flag; - height(n) = null_flag; - width(n) = null_flag; - rule_dir(n) = -1; - rule_index(n) = 0; - rule_transform(n) = 0; - break; - case whatsit_node: - if (j == open_node) { - open_name(n) = get_nullstr(); - open_area(n) = open_name(n); - open_ext(n) = open_name(n); - } - break; - case unset_node: - width(n) = null_flag; - break; - case pseudo_line_node: - case shape_node: - /* this is a trick that makes |pseudo_files| slightly slower, - but the overall allocation faster then an explicit test - at the top of |new_node()|. - */ - if (j>0) { - free_node(n, variable_node_size); - n = slow_get_node(j); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1))); - } - break; - default: - break; - } - if (synctex_par) { - /* handle synctex extension */ - switch (i) { - case math_node: - synctex_tag_math(n) = cur_input.synctex_tag_field; - synctex_line_math(n) = line; - break; - case glue_node: - synctex_tag_glue(n) = cur_input.synctex_tag_field; - synctex_line_glue(n) = line; - break; - case kern_node: - if (j != 0) { - synctex_tag_kern(n) = cur_input.synctex_tag_field; - synctex_line_kern(n) = line; - } - break; - case hlist_node: - case vlist_node: - case unset_node: - synctex_tag_box(n) = cur_input.synctex_tag_field; - synctex_line_box(n) = line; - break; - case rule_node: - synctex_tag_rule(n) = cur_input.synctex_tag_field; - synctex_line_rule(n) = line; - break; - } - } - /* take care of attributes */ - if (nodetype_has_attributes(i)) { - build_attribute_list(n); - /* lua_properties_set */ - } - type(n) = (quarterword) i; - subtype(n) = (quarterword) j; - return n; -} - -halfword raw_glyph_node(void) -{ - register halfword n = get_node(glyph_node_size); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); - type(n) = glyph_node; - subtype(n) = 0; - return n; -} - -halfword new_glyph_node(void) -{ - register halfword n = get_node(glyph_node_size); - (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); - type(n) = glyph_node; - subtype(n) = 0; - build_attribute_list(n); - /* lua_properties_set */ - return n; -} - -@ makes a duplicate of the node list that starts at |p| and returns a -pointer to the new list - -@c -halfword do_copy_node_list(halfword p, halfword end) -{ - halfword q = null; /* previous position in new list */ - halfword h = null; /* head of the list */ - register halfword s ; - lua_properties_push; /* saves stack and time */ - while (p != end) { - s = copy_node(p); - if (h == null) { - h = s; - } else { - couple_nodes(q, s); - } - q = s; - p = vlink(p); - } - lua_properties_pop; /* saves stack and time */ - return h; -} - -halfword copy_node_list(halfword p) -{ - return do_copy_node_list(p, null); -} - -#define copy_sub_list(target,source) do { \ - if (source != null) { \ - s = do_copy_node_list(source, null); \ - target = s; \ - } else { \ - target = null; \ - } \ - } while (0) - -#define copy_sub_node(target,source) do { \ - if (source != null) { \ - s = copy_node(source); \ - target = s ; \ - } else { \ - target = null; \ - } \ -} while (0) - -@ make a dupe of a single node - -@c -static void copy_node_wrapup_core(halfword p, halfword r) -{ - halfword s ; - switch (subtype(p)) { - case write_node: - case special_node: - add_token_ref(write_tokens(p)); - break; - case late_lua_node: - copy_late_lua(r, p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - add_node_attr_ref(user_node_value(p)); - break; - case 'l': - copy_user_lua(r, p); - break; - case 'n': - s = copy_node_list(user_node_value(p)); - user_node_value(r) = s; - break; - case 's': - /* |add_string_ref(user_node_value(p));| */ - break; - case 't': - add_token_ref(user_node_value(p)); - break; - } - break; - default: - break ; - } -} - -void copy_node_wrapup_dvi(halfword p, halfword r) -{ -} - -void copy_node_wrapup_pdf(halfword p, halfword r) -{ - switch(subtype(p)) { - case pdf_literal_node: - copy_pdf_literal(r, p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - add_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - add_token_ref(pdf_setmatrix_data(p)); - break; - case pdf_annot_node: - add_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(r) != null) - add_token_ref(pdf_link_attr(r)); - add_action_ref(pdf_link_action(r)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - add_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - add_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - add_token_ref(pdf_thread_attr(p)); - break; - default: - break; - } -} - -halfword copy_node(const halfword p) -{ - halfword r; /* current node being fabricated for new list */ - halfword w; /* whatsit subtype */ - halfword t; /* type of node */ - register halfword s; /* a helper variable for copying into variable mem */ - register int i; - if (copy_error(p)) { - r = new_node(temp_node, 0); - return r; - } - t = type(p); - i = get_node_size(t,subtype(p)); - r = get_node(i); - - (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i)); - - /* possible speedup: */ - /* - if t == glue_spec) { - return r; - } - */ - - if (synctex_par) { - /* handle synctex extension */ - switch (t) { - case math_node: - synctex_tag_math(r) = cur_input.synctex_tag_field; - synctex_line_math(r) = line; - break; - case kern_node: - synctex_tag_kern(r) = cur_input.synctex_tag_field; - synctex_line_kern(r) = line; - break; - } - } - if (nodetype_has_attributes(t)) { - add_node_attr_ref(node_attr(p)); - alink(r) = null; - lua_properties_copy(r,p); - } - vlink(r) = null; - - switch (t) { - case glyph_node: - copy_sub_list(lig_ptr(r),lig_ptr(p)) ; - break; - case glue_node: - copy_sub_list(leader_ptr(r),leader_ptr(p)) ; - break; - case hlist_node: - case vlist_node: - case unset_node: - copy_sub_list(list_ptr(r),list_ptr(p)) ; - break; - case disc_node: - pre_break(r) = pre_break_head(r); - if (vlink_pre_break(p) != null) { - s = copy_node_list(vlink_pre_break(p)); - alink(s) = pre_break(r); - tlink_pre_break(r) = tail_of_list(s); - vlink_pre_break(r) = s; - } else { - assert(tlink(pre_break(r)) == null); - } - post_break(r) = post_break_head(r); - if (vlink_post_break(p) != null) { - s = copy_node_list(vlink_post_break(p)); - alink(s) = post_break(r); - tlink_post_break(r) = tail_of_list(s); - vlink_post_break(r) = s; - } else { - assert(tlink_post_break(r) == null); - } - no_break(r) = no_break_head(r); - if (vlink(no_break(p)) != null) { - s = copy_node_list(vlink_no_break(p)); - alink(s) = no_break(r); - tlink_no_break(r) = tail_of_list(s); - vlink_no_break(r) = s; - } else { - assert(tlink_no_break(r) == null); - } - break; - case math_node: - break; - case ins_node: - copy_sub_list(ins_ptr(r),ins_ptr(p)) ; - break; - case margin_kern_node: - copy_sub_node(margin_char(r),margin_char(p)); - break; - case mark_node: - add_token_ref(mark_ptr(p)); - break; - case adjust_node: - copy_sub_list(adjust_ptr(r),adjust_ptr(p)); - break; - case choice_node: - copy_sub_list(display_mlist(r),display_mlist(p)) ; - copy_sub_list(text_mlist(r),text_mlist(p)) ; - copy_sub_list(script_mlist(r),script_mlist(p)) ; - copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; - break; - case simple_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - break; - case radical_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - copy_sub_node(left_delimiter(r),left_delimiter(p)) ; - copy_sub_list(degree(r),degree(p)) ; - break; - case accent_noad: - copy_sub_list(nucleus(r),nucleus(p)) ; - copy_sub_list(subscr(r),subscr(p)) ; - copy_sub_list(supscr(r),supscr(p)) ; - copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ; - copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; - copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ; - break; - case fence_noad: - copy_sub_node(delimiter(r),delimiter(p)) ; - break; - case sub_box_node: - case sub_mlist_node: - copy_sub_list(math_list(r),math_list(p)) ; - break; - case fraction_noad: - copy_sub_list(numerator(r),numerator(p)) ; - copy_sub_list(denominator(r),denominator(p)) ; - copy_sub_node(left_delimiter(r),left_delimiter(p)) ; - copy_sub_node(right_delimiter(r),right_delimiter(p)) ; - break; - case glue_spec_node: - case dir_node: - case local_par_node: - case boundary_node: - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - copy_node_wrapup_pdf(p,r); - } else if (w >= backend_first_dvi_whatsit) { - copy_node_wrapup_dvi(p,r); - } else { - copy_node_wrapup_core(p,r); - } - break; - } - return r; -} - -/* x */ - -#define free_sub_list(source) if (source != null) flush_node_list(source); -#define free_sub_node(source) if (source != null) flush_node(source); - -@ @c - -static void flush_node_wrapup_core(halfword p) -{ - switch (subtype(p)) { - case open_node: - case write_node: - case close_node: - case save_pos_node: - break; - case special_node: - delete_token_ref(write_tokens(p)); - break; - case late_lua_node: - free_late_lua(p); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - delete_attribute_ref(user_node_value(p)); - break; - case 'd': - break; - case 'l': - free_user_lua(user_node_value(p)); - break; - case 'n': - flush_node_list(user_node_value(p)); - break; - case 's': - /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ - break; - case 't': - delete_token_ref(user_node_value(p)); - break; - default: - { - const char *hlp[] = { - "The type of the value in a user defined whatsit node should be one", - "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", - "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", - "know how to free the node's value. A memory leak may result.", - NULL - }; - tex_error("Unidentified user defined whatsit", hlp); - } - break; - } - break; - } -} - -void flush_node_wrapup_dvi(halfword p) -{ -} - -void flush_node_wrapup_pdf(halfword p) -{ - switch(subtype(p)) { - case pdf_save_node: - case pdf_restore_node: - case pdf_refobj_node: - case pdf_end_link_node: - case pdf_end_thread_node: - break; - case pdf_literal_node: - free_pdf_literal(p); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - delete_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - delete_token_ref(pdf_setmatrix_data(p)); - break; - case pdf_annot_node: - delete_token_ref(pdf_annot_data(p)); - break; - case pdf_link_data_node: - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - delete_token_ref(pdf_link_attr(p)); - delete_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - delete_token_ref(pdf_dest_id(p)); - break; - case pdf_action_node: - if (pdf_action_type(p) == pdf_action_user) { - delete_token_ref(pdf_action_tokens(p)); - } else { - if (pdf_action_file(p) != null) - delete_token_ref(pdf_action_file(p)); - if (pdf_action_type(p) == pdf_action_page) - delete_token_ref(pdf_action_tokens(p)); - else if (pdf_action_named_id(p) > 0) - delete_token_ref(pdf_action_id(p)); - } - break; - case pdf_thread_data_node: - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - delete_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - delete_token_ref(pdf_thread_attr(p)); - break; - } -} - -void flush_node(halfword p) -{ - halfword w; - if (p == null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - switch (type(p)) { - case glyph_node: - free_sub_list(lig_ptr(p)); - break; - case glue_node: - free_sub_list(leader_ptr(p)); - break; - case hlist_node: - case vlist_node: - case unset_node: - free_sub_list(list_ptr(p)); - break; - case disc_node: - /* watch the start at temp node hack */ - free_sub_list(vlink(pre_break(p))); - free_sub_list(vlink(post_break(p))); - free_sub_list(vlink(no_break(p))); - break; - case rule_node: - case kern_node: - case penalty_node: - case math_node: - break; - case glue_spec_node: - /* this allows free-ing of lua-allocated glue specs */ -//if (valid_node(p)) { -// free_node(p, subtype(p)); -//} -// return ; - break ; - case dir_node: - case local_par_node: - case boundary_node: - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - flush_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - flush_node_wrapup_dvi(p); - } else { - flush_node_wrapup_core(p); - } - break; - case ins_node: - flush_node_list(ins_ptr(p)); - break; - case margin_kern_node: - flush_node(margin_char(p)); - break; - case mark_node: - delete_token_ref(mark_ptr(p)); - break; - case adjust_node: - flush_node_list(adjust_ptr(p)); - break; - case style_node: /* nothing to do */ - break; - case choice_node: - free_sub_list(display_mlist(p)); - free_sub_list(text_mlist(p)); - free_sub_list(script_mlist(p)); - free_sub_list(script_script_mlist(p)); - break; - case simple_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - break; - case radical_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - free_sub_node(left_delimiter(p)); - free_sub_list(degree(p)); - break; - case accent_noad: - free_sub_list(nucleus(p)); - free_sub_list(subscr(p)); - free_sub_list(supscr(p)); - free_sub_list(top_accent_chr(p)); - free_sub_list(bot_accent_chr(p)); - free_sub_list(overlay_accent_chr(p)); - break; - case fence_noad: - free_sub_list(delimiter(p)); - break; - case delim_node: /* nothing to do */ - case math_char_node: - case math_text_char_node: - break; - case sub_box_node: - case sub_mlist_node: - free_sub_list(math_list(p)); - break; - case fraction_noad: - free_sub_list(numerator(p)); - free_sub_list(denominator(p)); - free_sub_node(left_delimiter(p)); - free_sub_node(right_delimiter(p)); - break; - case pseudo_file_node: - free_sub_list(pseudo_lines(p)); - break; - case pseudo_line_node: - case shape_node: - free_node(p, subtype(p)); - return; - break; - case align_stack_node: - case span_node: - case movement_node: - case if_node: - case nesting_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case inserting_node: - case split_up_node: - case expr_node: - case attribute_node: - case attribute_list_node: - case temp_node: - break; - default: - formatted_error("nodes","flushing weird node type %d", type(p)); - return; - } - if (nodetype_has_attributes(type(p))) { - delete_attribute_ref(node_attr(p)); - lua_properties_reset(p); - } - free_node(p, get_node_size(type(p), subtype(p))); - return; -} - -@ @c -void flush_node_list(halfword pp) -{ /* erase list of nodes starting at |p| */ - register halfword p = pp; - if (p == null) /* legal, but no-op */ - return; - if (free_error(p)) - return; - lua_properties_push; /* saves stack and time */ - while (p != null) { - register halfword q = vlink(p); - flush_node(p); - p = q; - } - lua_properties_pop; /* saves stack and time */ -} - -@ @c -static void check_node_wrapup_core(halfword p) -{ - switch (subtype(p)) { - /* frontend code */ - case special_node: - check_token_ref(write_tokens(p)); - break; - case user_defined_node: - switch (user_node_type(p)) { - case 'a': - check_attribute_ref(user_node_value(p)); - break; - case 't': - check_token_ref(user_node_value(p)); - break; - case 'n': - dorangetest(p, user_node_value(p), var_mem_max); - break; - case 's': - case 'd': - break; - default: - confusion("unknown user node type"); - break; - } - break; - case open_node: - case write_node: - case close_node: - case save_pos_node: - break; - } -} - -void check_node_wrapup_dvi(halfword p) -{ -} - -void check_node_wrapup_pdf(halfword p) -{ - switch (subtype(p)) { - case pdf_literal_node: - if (pdf_literal_type(p) == normal) - check_token_ref(pdf_literal_data(p)); - break; - case pdf_colorstack_node: - if (pdf_colorstack_cmd(p) <= colorstack_data) - check_token_ref(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - check_token_ref(pdf_setmatrix_data(p)); - break; - case late_lua_node: - if (late_lua_name(p) > 0) - check_token_ref(late_lua_name(p)); - if (late_lua_type(p) == normal) - check_token_ref(late_lua_data(p)); - break; - case pdf_annot_node: - check_token_ref(pdf_annot_data(p)); - break; - case pdf_start_link_node: - if (pdf_link_attr(p) != null) - check_token_ref(pdf_link_attr(p)); - check_action_ref(pdf_link_action(p)); - break; - case pdf_dest_node: - if (pdf_dest_named_id(p) > 0) - check_token_ref(pdf_dest_id(p)); - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (pdf_thread_named_id(p) > 0) - check_token_ref(pdf_thread_id(p)); - if (pdf_thread_attr(p) != null) - check_token_ref(pdf_thread_attr(p)); - break; - case pdf_save_node: - case pdf_restore_node: - case pdf_refobj_node: - case pdf_end_link_node: - case pdf_end_thread_node: - break; - default: - confusion("wrapup pdf nodes"); - break; - } -} - -void check_node(halfword p) -{ - halfword w ; - switch (type(p)) { - case glyph_node: - dorangetest(p, lig_ptr(p), var_mem_max); - break; - case glue_node: - dorangetest(p, leader_ptr(p), var_mem_max); - break; - case hlist_node: - case vlist_node: - case unset_node: - case align_record_node: - dorangetest(p, list_ptr(p), var_mem_max); - break; - case ins_node: - dorangetest(p, ins_ptr(p), var_mem_max); - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - check_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - check_node_wrapup_dvi(p); - } else { - check_node_wrapup_core(p); - } - break; - case margin_kern_node: - check_node(margin_char(p)); - break; - case math_node: - break; - case disc_node: - dorangetest(p, vlink(pre_break(p)), var_mem_max); - dorangetest(p, vlink(post_break(p)), var_mem_max); - dorangetest(p, vlink(no_break(p)), var_mem_max); - break; - case adjust_node: - dorangetest(p, adjust_ptr(p), var_mem_max); - break; - case pseudo_file_node: - dorangetest(p, pseudo_lines(p), var_mem_max); - break; - case pseudo_line_node: - case shape_node: - break; - case choice_node: - dorangetest(p, display_mlist(p), var_mem_max); - dorangetest(p, text_mlist(p), var_mem_max); - dorangetest(p, script_mlist(p), var_mem_max); - dorangetest(p, script_script_mlist(p), var_mem_max); - break; - case fraction_noad: - dorangetest(p, numerator(p), var_mem_max); - dorangetest(p, denominator(p), var_mem_max); - dorangetest(p, left_delimiter(p), var_mem_max); - dorangetest(p, right_delimiter(p), var_mem_max); - break; - case simple_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - break; - case radical_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - dorangetest(p, degree(p), var_mem_max); - dorangetest(p, left_delimiter(p), var_mem_max); - break; - case accent_noad: - dorangetest(p, nucleus(p), var_mem_max); - dorangetest(p, subscr(p), var_mem_max); - dorangetest(p, supscr(p), var_mem_max); - dorangetest(p, top_accent_chr(p), var_mem_max); - dorangetest(p, bot_accent_chr(p), var_mem_max); - dorangetest(p, overlay_accent_chr(p), var_mem_max); - break; - case fence_noad: - dorangetest(p, delimiter(p), var_mem_max); - break; - /* - case rule_node: - case kern_node: - case penalty_node: - case mark_node: - case style_node: - case attribute_list_node: - case attribute_node: - case glue_spec_node: - case temp_node: - case align_stack_node: - case movement_node: - case if_node: - case nesting_node: - case span_node: - case unhyphenated_node: - case hyphenated_node: - case delta_node: - case passive_node: - case expr_node: - case dir_node: - case boundary_node: - case local_par_node: - break; - default: - fprintf(stdout, "check_node: type is %d\n", type(p)); - */ - } -} - -@ @c -void fix_node_list(halfword head) -{ - halfword p, q; - if (head == null) - return; - p = head; - q = vlink(p); - while (q != null) { - alink(q) = p; - p = q; - q = vlink(p); - } -} - -@ @c -halfword get_node(int s) -{ - register halfword r; - - if (s < MAX_CHAIN_SIZE) { - r = free_chain[s]; - if (r != null) { - free_chain[s] = vlink(r); -#ifdef CHECK_NODE_USAGE - varmem_sizes[r] = (char) s; -#endif - vlink(r) = null; - var_used += s; /* maintain usage statistics */ - return r; - } - /* this is the end of the 'inner loop' */ - return slow_get_node(s); - } else { - normal_error("nodes","there is a problem in getting a node, case 1"); - return null; - } -} - -@ @c -void free_node(halfword p, int s) -{ - if (p <= my_prealloc) { - formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p)); - return; - } -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - if (s < MAX_CHAIN_SIZE) { - vlink(p) = free_chain[s]; - free_chain[s] = p; - } else { - /* todo ? it is perhaps possible to merge this node with an existing rover */ - node_size(p) = s; - vlink(p) = rover; - while (vlink(rover) != vlink(p)) { - rover = vlink(rover); - } - vlink(rover) = p; - } - /* maintain statistics */ - var_used -= s; -} - -@ @c -static void free_node_chain(halfword q, int s) -{ - register halfword p = q; - while (vlink(p) != null) { -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - var_used -= s; - p = vlink(p); - } - var_used -= s; -#ifdef CHECK_NODE_USAGE - varmem_sizes[p] = 0; -#endif - vlink(p) = free_chain[s]; - free_chain[s] = q; -} - -@ At the start of the node memory area we reserve some special nodes, -for instance frequently used glue specifications. We could as well just -use new_glue here but for the moment we stick to the traditional approach. - -@c -#define initialize_glue(n,wi,st,sh,sto,sho) \ - vlink(n) = null; \ - type(n) = glue_spec_node; \ - width(n) = wi; \ - stretch(n) = st; \ - shrink(n) = sh; \ - stretch_order(n) = sto; \ - shrink_order(n) = sho; - -#define initialize_whatever(n,t) \ - vinfo(n) = 0; \ - type(n) = t; \ - vlink(n) = null; \ - alink(n) = null; - -#define initialize_point(n) \ - type(n) = glyph_node; \ - subtype(n) = 0; \ - vlink(n) = null; \ - vinfo(n + 1) = null; \ - alink(n) = null; \ - font(n) = 0; \ - character(n) = '.'; \ - vinfo(n + 3) = 0; \ - vlink(n + 3) = 0; \ - vinfo(n + 4) = 0; \ - vlink(n + 4) = 0; - -void init_node_mem(int t) -{ - my_prealloc = var_mem_stat_max; - - varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t); - if (varmem == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); -#ifdef CHECK_NODE_USAGE - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); - if (varmem_sizes == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); -#endif - var_mem_max = t; - rover = var_mem_stat_max + 1; - vlink(rover) = rover; - node_size(rover) = (t - rover); - var_used = 0; - - /* initialize static glue specs */ - - initialize_glue(zero_glue,0,0,0,0,0); - initialize_glue(sfi_glue,0,0,0,sfi,0); - initialize_glue(fil_glue,0,unity,0,fil,0); - initialize_glue(fill_glue,0,unity,0,fill,0); - initialize_glue(ss_glue,0,unity,unity,fil,fil); - initialize_glue(fil_neg_glue,0,-unity,0,fil,0); - - /* initialize node list heads */ - - initialize_whatever(page_ins_head,temp_node); - initialize_whatever(contrib_head,temp_node); - initialize_whatever(page_head,temp_node); - initialize_whatever(temp_head,temp_node); - initialize_whatever(hold_head,temp_node); - initialize_whatever(adjust_head,temp_node); - initialize_whatever(pre_adjust_head,temp_node); - initialize_whatever(align_head,temp_node); - - initialize_whatever(active,unhyphenated_node); - initialize_whatever(end_span,span_node); - - initialize_point(begin_point); - initialize_point(end_point); -} - -@ @c -void dump_node_mem(void) -{ - dump_int(var_mem_max); - dump_int(rover); - dump_things(varmem[0], var_mem_max); -#ifdef CHECK_NODE_USAGE - dump_things(varmem_sizes[0], var_mem_max); -#endif - dump_things(free_chain[0], MAX_CHAIN_SIZE); - dump_int(var_used); - dump_int(my_prealloc); -} - -@ it makes sense to enlarge the varmem array immediately -@c - -void undump_node_mem(void) -{ - int x; - undump_int(x); - undump_int(rover); - var_mem_max = (x < 100000 ? 100000 : x); - varmem = xmallocarray(memory_word, (unsigned) var_mem_max); - undump_things(varmem[0], x); -#ifdef CHECK_NODE_USAGE - varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); - memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); - undump_things(varmem_sizes[0], x); -#endif - undump_things(free_chain[0], MAX_CHAIN_SIZE); - undump_int(var_used); - undump_int(my_prealloc); - if (var_mem_max > x) { - /* todo ? it is perhaps possible to merge the new node with an existing rover */ - vlink(x) = rover; - node_size(x) = (var_mem_max - x); - while (vlink(rover) != vlink(x)) { - rover = vlink(rover); - } - vlink(rover) = x; - } -} - -@ @c -halfword slow_get_node(int s) -{ - register int t; - - RETRY: - t = node_size(rover); - if (vlink(rover) < var_mem_max && vlink(rover) != 0) { - if (t > s) { - /* allocating from the bottom helps decrease page faults */ - register halfword r = rover; - rover += s; - vlink(rover) = vlink(r); - node_size(rover) = node_size(r) - s; - if (vlink(rover) != r) { /* list is longer than one */ - halfword q = r; - while (vlink(q) != r) { - q = vlink(q); - } - vlink(q) += s; - } else { - vlink(rover) += s; - } - if (vlink(rover) < var_mem_max) { -#ifdef CHECK_NODE_USAGE - varmem_sizes[r] = (char) (s > 127 ? 127 : s); -#endif - vlink(r) = null; - var_used += s; /* maintain usage statistics */ - return r; /* this is the only exit */ - } else { - normal_error("nodes","there is a problem in getting a node, case 2"); - return null; - } - } else { - /* attempt to keep the free list small */ - int x; - if (vlink(rover) != rover) { - if (t < MAX_CHAIN_SIZE) { - halfword l = vlink(rover); - vlink(rover) = free_chain[t]; - free_chain[t] = rover; - rover = l; - while (vlink(l) != free_chain[t]) { - l = vlink(l); - } - vlink(l) = rover; - goto RETRY; - } else { - halfword l = rover; - while (vlink(rover) != l) { - if (node_size(rover) > s) { - goto RETRY; - } - rover = vlink(rover); - } - } - } - /* if we are still here, it was apparently impossible to get a match */ - x = (var_mem_max >> 2) + s; - varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x)); - if (varmem == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word)); -#ifdef CHECK_NODE_USAGE - varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x)); - if (varmem_sizes == NULL) { - overflow("node memory size", (unsigned) var_mem_max); - } - memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char)); -#endif - /* todo ? it is perhaps possible to merge the new memory with an existing rover */ - vlink(var_mem_max) = rover; - node_size(var_mem_max) = x; - while (vlink(rover) != vlink(var_mem_max)) { - rover = vlink(rover); - } - vlink(rover) = var_mem_max; - rover = var_mem_max; - var_mem_max += x; - goto RETRY; - } - } else { - normal_error("nodes","there is a problem in getting a node, case 3"); - return null; - } -} - -@ @c -char *sprint_node_mem_usage(void) -{ - char *s; -#ifdef CHECK_NODE_USAGE - char *ss; - int i; - int b = 0; - char msg[256]; - int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; - s = strdup(""); - for (i = (var_mem_max - 1); i > my_prealloc; i--) { - if (varmem_sizes[i] > 0) { - if (type(i) > last_normal_node + last_whatsit_node) { - node_counts[last_normal_node + last_whatsit_node + 1]++; - } else if (type(i) == whatsit_node) { - node_counts[(subtype(i) + last_normal_node + 1)]++; - } else { - node_counts[type(i)]++; - } - } - } - for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { - if (node_counts[i] > 0) { - int j = - (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); - snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], - get_node_name((i > last_normal_node ? whatsit_node : i), j)); - ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); - strcpy(ss, s); - strcat(ss, msg); - free(s); - s = ss; - b = 1; - } - } -#else - s = strdup(""); -#endif - return s; -} - -@ @c -halfword list_node_mem_usage(void) -{ - halfword q = null; -#ifdef CHECK_NODE_USAGE - halfword p = null; - halfword i, j; - char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); - memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); - for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { - if (saved_varmem_sizes[i] > 0) { - j = copy_node(i); - if (p == null) { - q = j; - } else { - vlink(p) = j; - } - p = j; - } - } - free(saved_varmem_sizes); -#endif - return q; -} - -@ @c -void print_node_mem_stats(void) -{ - int i, b; - halfword j; - char msg[256]; - char *s; - int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; - snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc)); - tprint_nl(msg); - s = sprint_node_mem_usage(); - tprint_nl(" "); - tprint(s); - free(s); - tprint(" nodes"); - tprint_nl(" avail lists: "); - b = 0; - for (i = 1; i < MAX_CHAIN_SIZE; i++) { - for (j = free_chain[i]; j != null; j = vlink(j)) - free_chain_counts[i]++; - if (free_chain_counts[i] > 0) { - snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]); - tprint(msg); - b = 1; - } - } - /* newline, if needed */ - print_nlp(); -} - -/* this belongs in the web but i couldn't find the correct syntactic place */ - -halfword new_span_node(halfword n, int s, scaled w) -{ - halfword p = new_node(span_node, 0); - span_link(p) = n; - span_span(p) = s; - width(p) = w; - return p; -} - -@* Attribute stuff. - -@c -static halfword new_attribute_node(unsigned int i, int v) -{ - register halfword r = get_node(attribute_node_size); - type(r) = attribute_node; - attribute_id(r) = (halfword) i; - attribute_value(r) = v; - /* not used but nicer in print */ - subtype(r) = 0; - return r; -} - -@ @c -halfword copy_attribute_list(halfword n) -{ - halfword q = get_node(attribute_node_size); - register halfword p = q; - type(p) = attribute_list_node; - attr_list_ref(p) = 0; - n = vlink(n); - while (n != null) { - register halfword r = get_node(attribute_node_size); - /* the link will be fixed automatically in the next loop */ - (void) memcpy((void *) (varmem + r), (void *) (varmem + n), - (sizeof(memory_word) * attribute_node_size)); - vlink(p) = r; - p = r; - n = vlink(n); - } - return q; -} - -@ @c -void update_attribute_cache(void) -{ - halfword p; - register int i; - attr_list_cache = get_node(attribute_node_size); - type(attr_list_cache) = attribute_list_node; - attr_list_ref(attr_list_cache) = 0; - p = attr_list_cache; - for (i = 0; i <= max_used_attr; i++) { - register int v = attribute(i); - if (v > UNUSED_ATTRIBUTE) { - register halfword r = new_attribute_node((unsigned) i, v); - vlink(p) = r; - p = r; - } - } - if (vlink(attr_list_cache) == null) { - free_node(attr_list_cache, attribute_node_size); - attr_list_cache = null; - } - return; -} - -@ @c -void build_attribute_list(halfword b) -{ - if (max_used_attr >= 0) { - if (attr_list_cache == cache_disabled|| attr_list_cache == null) { - update_attribute_cache(); - if (attr_list_cache == null) - return; - } - attr_list_ref(attr_list_cache)++; - node_attr(b) = attr_list_cache; - } -} - -@ @c -halfword current_attribute_list(void) -{ - if (max_used_attr >= 0) { - if (attr_list_cache == cache_disabled) { - update_attribute_cache(); - } - return attr_list_cache ; - } - return null ; -} - - -@ @c -void reassign_attribute(halfword n, halfword new) -{ - halfword old; - old = node_attr(n); - if (new == null) { - /* there is nothing to assign but we need to check for an old value */ - if (old != null) - delete_attribute_ref(old); /* also nulls attr field of n */ - } else if (old == null) { - /* nothing is assigned so we just do that now */ - assign_attribute_ref(n,new); - } else if (old != new) { - /* something is assigned so we need to clean up and assign then */ - delete_attribute_ref(old); - assign_attribute_ref(n,new); - } - /* else: same value so there is no need to assign and change the refcount */ - node_attr(n) = new ; -} - -@ @c -void delete_attribute_ref(halfword b) -{ - if (b != null) { - if (type(b) == attribute_list_node){ - attr_list_ref(b)--; - if (attr_list_ref(b) == 0) { - if (b == attr_list_cache) - attr_list_cache = cache_disabled; - free_node_chain(b, attribute_node_size); - } - /* maintain sanity */ - if (attr_list_ref(b) < 0) { - attr_list_ref(b) = 0; - } - } else { - normal_error("nodes","trying to delete an attribute reference of a non attribute node"); - } - } -} - -void reset_node_properties(halfword b) -{ - if (b != null) { - lua_properties_reset(b); - } -} - -@ |p| is an attr list head, or zero -@c -halfword do_set_attribute(halfword p, int i, int val) -{ - register halfword q; - register int j = 0; - if (p == null) { /* add a new head \& node */ - q = get_node(attribute_node_size); - type(q) = attribute_list_node; - attr_list_ref(q) = 1; - p = new_attribute_node((unsigned) i, val); - vlink(q) = p; - return q; - } - q = p; - if (vlink(p) != null) { - while (vlink(p) != null) { - int t = attribute_id(vlink(p)); - if (t == i && attribute_value(vlink(p)) == val) - return q; /* no need to do anything */ - if (t >= i) - break; - j++; - p = vlink(p); - } - - p = q; - while (j-- > 0) - p = vlink(p); - if (attribute_id(vlink(p)) == i) { - attribute_value(vlink(p)) = val; - } else { /* add a new node */ - halfword r = new_attribute_node((unsigned) i, val); - vlink(r) = vlink(p); - vlink(p) = r; - } - return q; - } else { - normal_error("nodes","trying to set an attribute fails, case 1"); - return null ; - } -} - -@ @c -void set_attribute(halfword n, int i, int val) -{ - register halfword p; - register int j = 0; - /* not all nodes can have an attribute list */ - if (!nodetype_has_attributes(type(n))) - return; - /* if we have no list, we create one and quit */ - p = node_attr(n); - if (p == null) { /* add a new head \& node */ - p = get_node(attribute_node_size); - type(p) = attribute_list_node; - attr_list_ref(p) = 1; - node_attr(n) = p; - p = new_attribute_node((unsigned) i, val); - vlink(node_attr(n)) = p; - return; - } - /* we check if we have this attribute already and quit if the value stays the same */ - if (vlink(p) != null) { - while (vlink(p) != null) { - int t = attribute_id(vlink(p)); - if (t == i && attribute_value(vlink(p)) == val) - return; - if (t >= i) - break; - j++; - p = vlink(p); - } - /* j has now the position (if found) .. we assume a sorted list ! */ - p = node_attr(n); - - if (attr_list_ref(p) == 0 ) { - /* the list is invalid i.e. freed already */ - formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n); - /* the still dangling list gets ref count 1 */ - attr_list_ref(p) = 1; - } else if (attr_list_ref(p) == 1) { - /* this can really happen HH-LS */ - if (p == attr_list_cache) { - /* we can invalidate the cache setting */ - /* attr_list_cache = cache_disabled */ - /* or save the list, as done below */ - p = copy_attribute_list(p); - node_attr(n) = p; - /* the copied list gets ref count 1 */ - attr_list_ref(p) = 1; - } - } else { - /* the list is used multiple times so we make a copy */ - p = copy_attribute_list(p); - /* we decrement the ref count or the original */ - delete_attribute_ref(node_attr(n)); - node_attr(n) = p; - /* the copied list gets ref count 1 */ - attr_list_ref(p) = 1; - } - - - /* we go to position j in the list */ - while (j-- > 0) - p = vlink(p); - /* if we have a hit we just set the value otherwise we add a new node */ - if (attribute_id(vlink(p)) == i) { - attribute_value(vlink(p)) = val; - } else { /* add a new node */ - halfword r = new_attribute_node((unsigned) i, val); - vlink(r) = vlink(p); - vlink(p) = r; - } - } else { - normal_error("nodes","trying to set an attribute fails, case 2"); - } -} - -@ @c -int unset_attribute(halfword n, int i, int val) -{ - register halfword p; - register int t; - register int j = 0; - - if (!nodetype_has_attributes(type(n))) - return null; - p = node_attr(n); - if (p == null) - return UNUSED_ATTRIBUTE; - if (attr_list_ref(p) == 0) { - formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n); - return UNUSED_ATTRIBUTE; - } - if (vlink(p) != null) { - while (vlink(p) != null) { - t = attribute_id(vlink(p)); - if (t > i) - return UNUSED_ATTRIBUTE; - if (t == i) { - p = vlink(p); - break; - } - j++; - p = vlink(p); - } - if (attribute_id(p) != i) - return UNUSED_ATTRIBUTE; - /* if we are still here, the attribute exists */ - p = node_attr(n); - if (attr_list_ref(p) > 1 || p == attr_list_cache) { - halfword q = copy_attribute_list(p); - if (attr_list_ref(p) > 1) { - delete_attribute_ref(node_attr(n)); - } - attr_list_ref(q) = 1; - node_attr(n) = q; - } - p = vlink(node_attr(n)); - while (j-- > 0) - p = vlink(p); - t = attribute_value(p); - if (val == UNUSED_ATTRIBUTE || t == val) { - attribute_value(p) = UNUSED_ATTRIBUTE; - } - return t; - } else { - normal_error("nodes","trying to unset an attribute fails"); - return null; - } -} - -@ @c -int has_attribute(halfword n, int i, int val) -{ - register halfword p; - if (!nodetype_has_attributes(type(n))) - return UNUSED_ATTRIBUTE; - p = node_attr(n); - if (p == null || vlink(p) == null) - return UNUSED_ATTRIBUTE; - p = vlink(p); - while (p != null) { - if (attribute_id(p) == i) { - int ret = attribute_value(p); - if (val == UNUSED_ATTRIBUTE || val == ret) - return ret; - return UNUSED_ATTRIBUTE; - } else if (attribute_id(p) > i) { - return UNUSED_ATTRIBUTE; - } - p = vlink(p); - } - return UNUSED_ATTRIBUTE; -} - -@ @c -void print_short_node_contents(halfword p) -{ - switch (type(p)) { - case hlist_node: - case vlist_node: - case ins_node: - case whatsit_node: - case mark_node: - case adjust_node: - case unset_node: - print_char('['); - print_char(']'); - break; - case rule_node: - print_char('|'); - break; - case glue_node: - if (! glue_is_zero(p)) - print_char(' '); - break; - case math_node: - print_char('$'); - break; - case disc_node: - short_display(vlink(pre_break(p))); - short_display(vlink(post_break(p))); - break; - } -} - -@ @c -static void show_pdftex_whatsit_rule_spec(int p) -{ - tprint("("); - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); -} - -@ Each new type of node that appears in our data structure must be capable -of being displayed, copied, destroyed, and so on. The routines that we -need for write-oriented whatsits are somewhat like those for mark nodes; -other extensions might, of course, involve more subtlety here. - -@c -static void print_write_whatsit(const char *s, pointer p) -{ - tprint_esc(s); - if (write_stream(p) < 16) - print_int(write_stream(p)); - else if (write_stream(p) == 16) - print_char('*'); - else - print_char('-'); -} - -@ @c -static void show_node_wrapup_core(int p) -{ - switch (subtype(p)) { - case open_node: - print_write_whatsit("openout", p); - print_char('='); - print_file_name(open_name(p), open_area(p), open_ext(p)); - break; - case write_node: - print_write_whatsit("write", p); - print_mark(write_tokens(p)); - break; - case close_node: - print_write_whatsit("closeout", p); - break; - case special_node: - tprint_esc("special"); - print_mark(write_tokens(p)); - break; - case late_lua_node: - show_late_lua(p); - break; - case save_pos_node: - tprint_esc("savepos"); - break; - case user_defined_node: - tprint_esc("whatsit"); - print_int(user_node_id(p)); - print_char('='); - switch (user_node_type(p)) { - case 'a': - tprint("<>"); - break; - case 'n': - tprint("["); - show_node_list(user_node_value(p)); - tprint("]"); - break; - case 's': - print_char('"'); - print(user_node_value(p)); - print_char('"'); - break; - case 't': - print_mark(user_node_value(p)); - break; - default: /* only 'd' */ - print_int(user_node_value(p)); - break; - } - break; - } -} - -void show_node_wrapup_dvi(int p) -{ -} - -void show_node_wrapup_pdf(int p) -{ - switch (subtype(p)) { - case pdf_literal_node: - show_pdf_literal(p); - break; - case pdf_colorstack_node: - tprint_esc("pdfcolorstack "); - print_int(pdf_colorstack_stack(p)); - switch (pdf_colorstack_cmd(p)) { - case colorstack_set: - tprint(" set "); - break; - case colorstack_push: - tprint(" push "); - break; - case colorstack_pop: - tprint(" pop"); - break; - case colorstack_current: - tprint(" current"); - break; - default: - confusion("colorstack"); - break; - } - if (pdf_colorstack_cmd(p) <= colorstack_data) - print_mark(pdf_colorstack_data(p)); - break; - case pdf_setmatrix_node: - tprint_esc("pdfsetmatrix"); - print_mark(pdf_setmatrix_data(p)); - break; - case pdf_save_node: - tprint_esc("pdfsave"); - break; - case pdf_restore_node: - tprint_esc("pdfrestore"); - break; - case pdf_refobj_node: - tprint_esc("pdfrefobj"); - if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { - if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { - tprint(" attr"); - lua_rawgeti(Luas, LUA_REGISTRYINDEX, - obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); - print_char(' '); - tprint((const char *) lua_tostring(Luas, -1)); - lua_pop(Luas, 1); - } - tprint(" stream"); - } - if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) - tprint(" file"); - if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { - lua_rawgeti(Luas, LUA_REGISTRYINDEX, - obj_obj_data(static_pdf, pdf_obj_objnum(p))); - print_char(' '); - tprint((const char *) lua_tostring(Luas, -1)); - lua_pop(Luas, 1); - } - break; - case pdf_annot_node: - tprint_esc("pdfannot"); - show_pdftex_whatsit_rule_spec(p); - print_mark(pdf_annot_data(p)); - break; - case pdf_start_link_node: - tprint_esc("pdfstartlink"); - show_pdftex_whatsit_rule_spec(p); - if (pdf_link_attr(p) != null) { - tprint(" attr"); - print_mark(pdf_link_attr(p)); - } - tprint(" action"); - if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { - tprint(" user"); - print_mark(pdf_action_tokens(pdf_link_action(p))); - return; - } - if (pdf_action_file(pdf_link_action(p)) != null) { - tprint(" file"); - print_mark(pdf_action_file(pdf_link_action(p))); - } - switch (pdf_action_type(pdf_link_action(p))) { - case pdf_action_goto: - if (pdf_action_named_id(pdf_link_action(p)) > 0) { - tprint(" goto name"); - print_mark(pdf_action_id(pdf_link_action(p))); - } else { - tprint(" goto num"); - print_int(pdf_action_id(pdf_link_action(p))); - } - break; - case pdf_action_page: - tprint(" page"); - print_int(pdf_action_id(pdf_link_action(p))); - print_mark(pdf_action_tokens(pdf_link_action(p))); - break; - case pdf_action_thread: - if (pdf_action_named_id(pdf_link_action(p)) > 0) { - tprint(" thread name"); - print_mark(pdf_action_id(pdf_link_action(p))); - } else { - tprint(" thread num"); - print_int(pdf_action_id(pdf_link_action(p))); - } - break; - default: - normal_error("pdf backend", "unknown action type for link"); - break; - } - break; - case pdf_end_link_node: - tprint_esc("pdfendlink"); - break; - case pdf_dest_node: - tprint_esc("pdfdest"); - if (pdf_dest_named_id(p) > 0) { - tprint(" name"); - print_mark(pdf_dest_id(p)); - } else { - tprint(" num"); - print_int(pdf_dest_id(p)); - } - print_char(' '); - switch (pdf_dest_type(p)) { - case pdf_dest_xyz: - tprint("xyz"); - if (pdf_dest_xyz_zoom(p) != null) { - tprint(" zoom"); - print_int(pdf_dest_xyz_zoom(p)); - } - break; - case pdf_dest_fitbh: - tprint("fitbh"); - break; - case pdf_dest_fitbv: - tprint("fitbv"); - break; - case pdf_dest_fitb: - tprint("fitb"); - break; - case pdf_dest_fith: - tprint("fith"); - break; - case pdf_dest_fitv: - tprint("fitv"); - break; - case pdf_dest_fitr: - tprint("fitr"); - show_pdftex_whatsit_rule_spec(p); - break; - case pdf_dest_fit: - tprint("fit"); - break; - default: - tprint("unknown!"); - break; - } - break; - case pdf_thread_node: - case pdf_start_thread_node: - if (subtype(p) == pdf_thread_node) - tprint_esc("pdfthread"); - else - tprint_esc("pdfstartthread"); - tprint("("); - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); - if (pdf_thread_attr(p) != null) { - tprint(" attr"); - print_mark(pdf_thread_attr(p)); - } - if (pdf_thread_named_id(p) > 0) { - tprint(" name"); - print_mark(pdf_thread_id(p)); - } else { - tprint(" num"); - print_int(pdf_thread_id(p)); - } - break; - case pdf_end_thread_node: - tprint_esc("pdfendthread"); - break; - default: - break; - } -} - -@ 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 - 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. - - -@ |str_room| need not be checked; see |show_box| - -@ Recursive calls on |show_node_list| therefore use the following pattern: -@c -#define node_list_display(A) do { \ - append_char('.'); \ - show_node_list(A); \ - flush_char(); \ -} while (0) - -#define node_list_display_x(A,B) do { \ - if ((B) != null) { \ - append_char('.'); \ - append_char(A); \ - append_char(' '); \ - show_node_list(B); \ - flush_char(); \ - flush_char(); \ - flush_char(); \ - } \ -} while (0) - -/* prints a node list symbolically */ - -void show_node_list(int p) -{ - int n = 0; /* the number of items already printed at this level */ - halfword w; - real g; /* a glue ratio, as a floating point number */ - if ((int) cur_length > depth_threshold) { - if (p > null) - tprint(" []"); /* indicate that there's been some truncation */ - return; - } - while (p != null) { - print_ln(); - print_current_string(); /* display the nesting history */ - if (tracing_online_par < -2) - print_int(p); - incr(n); - if (n > breadth_max) { /* time to stop */ - tprint("etc."); - return; - } - /* Display node |p| */ - if (is_char_node(p)) { - print_font_and_char(p); - if (is_ligature(p)) { - /* Display ligature |p|; */ - tprint(" (ligature "); - if (is_leftboundary(p)) - print_char('|'); - font_in_short_display = font(p); - short_display(lig_ptr(p)); - if (is_rightboundary(p)) - print_char('|'); - print_char(')'); - } - } else { - switch (type(p)) { - case hlist_node: - case vlist_node: - case unset_node: - /* Display box |p|; */ - if (type(p) == hlist_node) - tprint_esc("h"); - else if (type(p) == vlist_node) - tprint_esc("v"); - else - tprint_esc("unset"); - tprint("box("); - print_scaled(height(p)); - print_char('+'); - print_scaled(depth(p)); - tprint(")x"); - print_scaled(width(p)); - if (type(p) == unset_node) { - /* Display special fields of the unset node |p|; */ - if (span_count(p) != min_quarterword) { - tprint(" ("); - print_int(span_count(p) + 1); - tprint(" columns)"); - } - if (glue_stretch(p) != 0) { - tprint(", stretch "); - print_glue(glue_stretch(p), glue_order(p), NULL); - } - if (glue_shrink(p) != 0) { - tprint(", shrink "); - print_glue(glue_shrink(p), glue_sign(p), NULL); - } - } else { - /* Display the value of |glue_set(p)| */ - /* The code will have to change in this place if |glue_ratio| is - a structured type instead of an ordinary |real|. Note that this routine - should avoid arithmetic errors even if the |glue_set| field holds an - arbitrary random value. The following code assumes that a properly - formed nonzero |real| number has absolute value $2^{20}$ or more when - it is regarded as an integer; this precaution was adequate to prevent - floating point underflow on the author's computer. - */ - - g = (real) (glue_set(p)); - if ((g != 0.0) && (glue_sign(p) != normal)) { - tprint(", glue set "); - if (glue_sign(p) == shrinking) - tprint("- "); - if (g > 20000.0 || g < -20000.0) { - if (g > 0.0) - print_char('>'); - else - tprint("< -"); - print_glue(20000 * unity, glue_order(p), NULL); - } else { - print_glue(round(unity * g), glue_order(p), NULL); - } - } - - if (shift_amount(p) != 0) { - tprint(", shifted "); - print_scaled(shift_amount(p)); - } - tprint(", direction "); - print_dir(box_dir(p)); - } - node_list_display(list_ptr(p)); /* recursive call */ - break; - case rule_node: - /* Display rule |p|; */ - if (subtype(p) == normal_rule) { - tprint_esc("rule("); - } else if (subtype(p) == empty_rule) { - tprint_esc("norule("); - } else if (subtype(p) == user_rule) { - tprint_esc("userrule("); - } else if (subtype(p) == box_rule) { - tprint_esc("box("); - } else if (subtype(p) == image_rule) { - tprint_esc("image("); - } - print_rule_dimen(height(p)); - print_char('+'); - print_rule_dimen(depth(p)); - tprint(")x"); - print_rule_dimen(width(p)); - break; - case ins_node: - /* Display insertion |p|; */ - tprint_esc("insert"); - print_int(subtype(p)); - tprint(", natural size "); - print_scaled(height(p)); - tprint("; split("); - print_spec(split_top_ptr(p), NULL); - print_char(','); - print_scaled(depth(p)); - tprint("); float cost "); - print_int(float_cost(p)); - node_list_display(ins_ptr(p)); /* recursive call */ - break; - case dir_node: - if (dir_dir(p) < 0) { - tprint_esc("enddir"); - print_char(' '); - print_dir(dir_dir(p) + dir_swap); - } else { - tprint_esc("begindir"); - print_char(' '); - print_dir(dir_dir(p)); - } - break; - case local_par_node: - tprint_esc("localpar"); - append_char('.'); - print_ln(); - print_current_string(); - tprint_esc("localinterlinepenalty"); - print_char('='); - print_int(local_pen_inter(p)); - print_ln(); - print_current_string(); - tprint_esc("localbrokenpenalty"); - print_char('='); - print_int(local_pen_broken(p)); - print_ln(); - print_current_string(); - tprint_esc("localleftbox"); - if (local_box_left(p) == null) { - tprint("=null"); - } else { - append_char('.'); - show_node_list(local_box_left(p)); - decr(cur_length); - } - print_ln(); - print_current_string(); - tprint_esc("localrightbox"); - if (local_box_right(p) == null) { - tprint("=null"); - } else { - append_char('.'); - show_node_list(local_box_right(p)); - decr(cur_length); - } - decr(cur_length); - break; - case boundary_node: - if (subtype(p)==0) { - tprint_esc("noboundary"); - } else { - switch (subtype(p)) { - case 1: - tprint_esc("boundary"); - break; - case 2: - tprint_esc("protrusionboundary"); - break; - case 3: - tprint_esc("wordboundary"); - break; - default: - tprint_esc("boundary"); - print_char(':'); - print_int(subtype(p)); - break; - } - print_char('='); - print_int(boundary_value(p)); - } - break; - case whatsit_node: - w = subtype(p) ; - if (w >= backend_first_pdf_whatsit) { - show_node_wrapup_pdf(p); - } else if (w >= backend_first_dvi_whatsit) { - show_node_wrapup_dvi(p); - } else { - show_node_wrapup_core(p); - } - break; - case glue_node: - /* Display glue |p|; */ - if (subtype(p) >= a_leaders) { - /* Display leaders |p|; */ - tprint_esc(""); - switch (subtype(p)) { - case a_leaders: - break; - case c_leaders: - print_char('c'); - break; - case x_leaders: - print_char('x'); - break; - case g_leaders: - print_char('g'); - break; - default: - normal_warning("nodes","weird glue leader subtype ignored"); - } - tprint("leaders "); - print_spec(p, NULL); - node_list_display(leader_ptr(p)); /* recursive call */ - } else { - tprint_esc("glue"); - if (subtype(p) != normal) { - print_char('('); - if ((subtype(p) - 1) < thin_mu_skip_code) { - print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1)); - } else if (subtype(p) < cond_math_glue) { - print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1)); - } else if (subtype(p) == cond_math_glue) { - tprint_esc("nonscript"); - } else { - tprint_esc("mskip"); - } - print_char(')'); - } - if (subtype(p) != cond_math_glue) { - print_char(' '); - if (subtype(p) < cond_math_glue) - print_spec(p, NULL); - else - print_spec(p, "mu"); - } - } - break; - case margin_kern_node: - tprint_esc("kern"); - print_scaled(width(p)); - if (subtype(p) == left_side) - tprint(" (left margin)"); - else - tprint(" (right margin)"); - break; - case kern_node: - /* Display kern |p|; */ - /* An ``explicit'' kern value is indicated implicitly by an explicit space. */ - if (subtype(p) != mu_glue) { - tprint_esc("kern"); - if (subtype(p) != normal) - print_char(' '); - print_scaled(width(p)); - if (subtype(p) == accent_kern) - tprint(" (for accent)"); - } else { - tprint_esc("mkern"); - print_scaled(width(p)); - tprint("mu"); - } - break; - case math_node: - /* Display math node |p|; */ - tprint_esc("math"); - if (subtype(p) == before) - tprint("on"); - else - tprint("off"); - if (!glue_is_zero(p)) { - tprint(", glued "); - print_spec(p, NULL); - } else if (surround(p) != 0) { - tprint(", surrounded "); - print_scaled(surround(p)); - } - break; - case penalty_node: - /* Display penalty |p|; */ - tprint_esc("penalty "); - print_int(penalty(p)); - break; - case disc_node: - /* Display discretionary |p|; */ - /* The |post_break| list of a discretionary node is indicated by a prefixed - `\.{\char'174}' instead of the `\..' before the |pre_break| list. */ - /* We're not compatible anyway so ... - tprint_esc("discretionary"); - print_int(disc_penalty(p)); - print_char('|'); - if (vlink(no_break(p)) != null) { - tprint(" replacing "); - node_list_display(vlink(no_break(p))); - } - node_list_display(vlink(pre_break(p))); - append_char('|'); - show_node_list(vlink(post_break(p))); - flush_char(); - */ - tprint_esc("discretionary"); - tprint(" (penalty "); - print_int(disc_penalty(p)); - print_char(')'); - node_list_display_x('<',vlink(pre_break(p))); - node_list_display_x('>',vlink(post_break(p))); - node_list_display_x('=',vlink(no_break(p))); - break; - case mark_node: - /* Display mark |p|; */ - tprint_esc("mark"); - if (mark_class(p) != 0) { - print_char('s'); - print_int(mark_class(p)); - } - print_mark(mark_ptr(p)); - break; - case adjust_node: - /* Display adjustment |p|; */ - tprint_esc("vadjust"); - if (subtype(p) != 0) - tprint(" pre "); - node_list_display(adjust_ptr(p)); /* recursive call */ - break; - case glue_spec_node: - tprint(""); - break; - default: - show_math_node(p); - break; - } - } - p = vlink(p); - } -} - -@ This routine finds the 'base' width of a horizontal box, using the same logic - that \TeX82 used for \.{\\predisplaywidth} */ - -@c -static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width) -{ - scaled d; /* increment to |v| */ - scaled w = -max_dimen; /* calculated |size| */ - scaled v = initial_width; /* |w| plus possible glue amount */ - while (p != null) { - if (is_char_node(p)) { - d = glyph_width(p); - goto FOUND; - } - switch (type(p)) { - case hlist_node: - case vlist_node: - case rule_node: - d = width(p); - goto FOUND; - break; - case margin_kern_node: - d = width(p); - break; - case kern_node: - d = width(p); - break; - case disc_node: - /* at the end of the line we should actually take the pre */ - if (no_break(p) != null) { - d = get_actual_box_width(r,vlink_no_break(p),0); - if (d <= -max_dimen || d >= max_dimen) { - d = 0; - } - } else { - d = 0; - } - goto FOUND; - break; - case math_node: - /* begin mathskip code */ - if (glue_is_zero(p)) { - d = surround(p); - break; - } else { - /* fall through */ - } - /* end mathskip code */ - case glue_node: - /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| - values, since such values are subject to system-dependent rounding. - System-dependent numbers are not allowed to infiltrate parameters like - |pre_display_size|, since \TeX82 is supposed to make the same decisions on all - machines. - */ - d = width(p); - if (glue_sign(r) == stretching) { - if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) - v = max_dimen; - } else if (glue_sign(r) == shrinking) { - if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) - v = max_dimen; - } - if (subtype(p) >= a_leaders) - goto FOUND; - break; - default: - d = 0; - break; - } - if (v < max_dimen) - v = v + d; - goto NOT_FOUND; - FOUND: - if (v < max_dimen) { - v = v + d; - w = v; - } else { - w = max_dimen; - break; - } - NOT_FOUND: - p = vlink(p); - } - return w; -} - -pointer actual_box_width(pointer r, scaled base_width) -{ - /* often this is the same as: - return + shift_amount(r) + base_width + - natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r)); - */ - return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width); -} - -@ @c -halfword tail_of_list(halfword p) -{ - halfword q = p; - while (vlink(q) != null) - q = vlink(q); - return q; -} - - - -@ @c -int var_used; - -@ Attribute lists need two extra globals to increase processing efficiency. -|max_used_attr| limits the test loop that checks for set attributes, and -|attr_list_cache| contains a pointer to an already created attribute list. It is -set to the special value |cache_disabled| when the current value can no longer be -trusted: after an assignment to an attribute register, and after a group has -ended. - -@c -int max_used_attr; /* maximum assigned attribute id */ -halfword attr_list_cache; - -@ 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|'. - -@ Character nodes appear only in horizontal lists, never in vertical lists. - -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|, -|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. - -@ 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|. - -@c -halfword new_null_box(void) -{ /* creates a new box node */ - halfword p = new_node(hlist_node, min_quarterword); - box_dir(p) = text_direction_par; - return p; -} - -@ A |vlist_node| is like an |hlist_node| in all respects except that it -contains a vertical list. - -@ 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. - -@ 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. - -@c -halfword new_rule(int s) -{ - halfword p = new_node(rule_node,s); - 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. - -@ A |mark_node| has a |mark_ptr| field that points to the reference count -of a token list that contains the user's \.{\\mark} text. -In addition there is a |mark_class| field that contains the mark class. - -@ 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. - -@ A |glyph_node|, which occurs only in horizontal lists, specifies a -glyph in a particular font, along with its attribute list. Older -versions of \TeX\ could use token memory for characters, because the -font,char combination would fit in a single word (both values were -required to be strictly less than $2^{16}$). In LuaTeX, room is -needed for characters that are larger than that, as well as a pointer -to a potential attribute list, and the two displacement values. - -In turn, that made the node so large that it made sense to merge -ligature glyphs as well, as that requires only one extra pointer. A -few extra classes of glyph nodes will be introduced later. The -unification of all those types makes it easier to manipulate lists of -glyphs. The subtype differentiates various glyph kinds. - -First, here is a function that returns a pointer to a glyph node for a given -glyph in a given font. If that glyph doesn't exist, |null| is returned -instead. Nodes of this subtype are directly created only for accents -and their base (through |make_accent|), and math nucleus items (in the -conversion from |mlist| to |hlist|). - -@c -halfword new_glyph(int f, int c) -{ - halfword p = null; /* the new node */ - if ((f == 0) || (char_exists(f, c))) { - p = new_glyph_node(); - set_to_glyph(p); - font(p) = f; - character(p) = c; - } - return p; -} - -@ A subset of the glyphs nodes represent ligatures: characters -fabricated from the interaction of two or more actual characters. The -characters that generated the ligature have not been forgotten, since -they are needed for diagnostic messages; 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 of these |glyph_node|s is 1, plus 2 and/or 1 if -the original source of the ligature included implicit left and/or -right boundaries. These nodes are created by the C function |new_ligkern|. - -A third general type of glyphs could be called a character, as it -only appears in lists that are not yet processed by the ligaturing and -kerning steps of the program. - -|main_control| inserts these, and they are later converted to -|subtype_normal| by |new_ligkern|. - -@c -quarterword norm_min(int h) -{ - if (h <= 0) - return 1; - else if (h >= 255) - return 255; - else - return (quarterword) h; -} - -halfword new_char(int f, int c) -{ - halfword p; /* the new node */ - p = new_glyph_node(); - set_to_character(p); - font(p) = f; - character(p) = c; - lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); - return p; -} - -@ Left and right ghost glyph nodes are the result of \.{\\leftghost} -and \.{\\rightghost}, respectively. They are going to be removed by -|new_ligkern|, at the end of which they are no longer needed. - -@ Here are a few handy helpers used by the list output routines. - -@c -scaled glyph_width(halfword p) -{ - scaled w = char_width(font(p), character(p)); - return w; -} - -scaled glyph_height(halfword p) -{ - scaled w = char_height(font(p), character(p)) + y_displace(p); - if (w < 0) - w = 0; - return w; -} - -scaled glyph_depth(halfword p) -{ - scaled w = char_depth(font(p), character(p)); - if (y_displace(p) > 0) - w = w - y_displace(p); - if (w < 0) - w = 0; - return w; -} - -@ 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 -|no_break(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 |no_break=null|. - -{TODO: Knuth said: All three of the discretionary texts must be lists -that consist entirely of character, kern, box and rule nodes.} - -If |subtype(p)=automatic_disc|, 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. - -@c -halfword new_disc(void) -{ /* creates an empty |disc_node| */ - halfword p = new_node(disc_node, 0); - disc_penalty(p) = hyphen_penalty_par; - 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. - -@ 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 |surround| field, which represents -the amount of surrounding space inserted by \.{\\mathsurround}. - -@c -halfword new_math(scaled w, int s) -{ - halfword p = new_node(math_node, s); - surround(p) = w; - return p; -} - -@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, -|rule_node|, |ins_node|, |mark_node|, |adjust_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. - -@ 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. - -@ 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|, |sfi|, |fil|, |fill|, or |filll|) -corresponding to the stretch and shrink values. - -@ 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. - -@c -halfword new_spec(halfword q) /* safeguard for copying a glue node */ -{ - if (q == null) { - return copy_node(zero_glue); - } else if (type(q) == glue_spec_node) { - return copy_node(q); - } else if (type(q) == glue_node) { - halfword p = copy_node(zero_glue); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; - } else { - /* alternatively we can issue a warning */ - return copy_node(zero_glue); - } -} - -@ 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}. - -@c -halfword new_param_glue(int n) -{ - halfword p = new_node(glue_node, n + 1); - halfword q = glue_par(n); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - return p; -} - -@ Glue nodes that are more or less anonymous are created by |new_glue|, -whose argument points to a glue specification. - -@c -halfword new_glue(halfword q) -{ - halfword p = new_node(glue_node, normal); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(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. -*/ - -@c -halfword new_skip_param(int n) -{ - halfword p = new_node(glue_node, n + 1); - halfword q = glue_par(n); - width(p) = width(q); - stretch(p) = stretch(q); - shrink(p) = shrink(q); - stretch_order(p) = stretch_order(q); - shrink_order(p) = shrink_order(q); - 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). - -@ The |new_kern| function creates a kern node having a given width. - -@c -halfword new_kern(scaled w) -{ - halfword p = new_node(kern_node, 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. - -@ Anyone who has been reading the last few sections of the program will -be able to guess what comes next. - -@c -halfword new_penalty(int m) -{ - halfword p = new_node(penalty_node, 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. - -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@> - -@ This function creates a |local_paragraph| node - -@c - -halfword make_local_par_node(int mode) -{ - int callback_id; - halfword q; - halfword p = new_node(local_par_node,0); - local_pen_inter(p) = local_inter_line_penalty_par; - local_pen_broken(p) = local_broken_penalty_par; - if (local_left_box_par != null) { - q = copy_node_list(local_left_box_par); - local_box_left(p) = q; - local_box_left_width(p) = width(local_left_box_par); - } - if (local_right_box_par != null) { - q = copy_node_list(local_right_box_par); - local_box_right(p) = q; - local_box_right_width(p) = width(local_right_box_par); - } - local_par_dir(p) = par_direction_par; - /* callback with node passed */ - callback_id = callback_defined(insert_local_par_callback); - if (callback_id > 0) { - int sfix = lua_gettop(Luas); - if (!get_callback(Luas, callback_id)) { - lua_settop(Luas, sfix); - return p; - } - nodelist_to_lua(Luas, p); - lua_push_local_par_mode(Luas,mode) - if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */ - char errmsg[256]; /* temp hack ... we will have a formatted error */ - snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1)); - errmsg[255]='\0'; - lua_settop(Luas, sfix); - normal_error("insert_local_par",errmsg); /* to be done */ - return p; - } - lua_settop(Luas, sfix); - } - /* done */ - return p; -} +% texnodes.w +% +% Copyright 2006-2010 Taco Hoekwater +% +% This file is part of LuaTeX. +% +% LuaTeX is free software; you can redistribute it and/or modify it under +% the terms of the GNU General Public License as published by the Free +% Software Foundation; either version 2 of the License, or (at your +% option) any later version. +% +% LuaTeX is distributed in the hope that it will be useful, but WITHOUT +% ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +% FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public +% License for more details. +% +% You should have received a copy of the GNU General Public License along +% with LuaTeX; if not, see . + +@ @c + +#include "ptexlib.h" +#include "lua/luatex-api.h" + +/* we can consider less mode sizes: 2 4 6 8 */ + +@ +This module started out using NDEBUG to trigger checking invalid node usage, +something that is needed because users can mess up nodes in Lua. At some point +that code was always enabled so it is now always on but still can be recognized +as additional code. And as the performance hit is close to zero so disabling +makes no sense, not even to make it configureable. There is a little more memory +used but that is neglectable compared to other memory usage. + +@c +#define MAX_CHAIN_SIZE 13 /* why not a bit larger */ +#define CHECK_NODE_USAGE 1 /* this triggers checking */ + +memory_word *volatile varmem = NULL; + +#ifdef CHECK_NODE_USAGE + char *varmem_sizes = NULL; +#endif + +halfword var_mem_max = 0; +halfword rover = 0; + +halfword free_chain[MAX_CHAIN_SIZE] = { null }; + +static int my_prealloc = 0; + +int fix_node_lists = 1; /* used in font and lang */ + +halfword slow_get_node(int s); /* defined below */ + +#define fake_node 100 +#define fake_node_size 2 +#define fake_node_name "fake" + +#define variable_node_size 2 + +/* core nodes */ + +const char *node_fields_list[] = { + "attr", "width", "depth", "height", "dir", "shift", "glue_order", "glue_sign", + "glue_set", "head", NULL +}; +const char *node_fields_rule[] = { + "attr", "width", "depth", "height", "dir", "index", NULL +}; +const char *node_fields_insert[] = { + "attr", "cost", "depth", "height", "spec", "head", NULL +}; +const char *node_fields_mark[] = { + "attr", "class", "mark", NULL +}; +const char *node_fields_adjust[] = { + "attr", "head", NULL +}; +const char *node_fields_disc[] = { + "attr", "pre", "post", "replace", "penalty", NULL +}; +const char *node_fields_math[] = { + "attr", "surround", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +}; +const char *node_fields_glue[] = { + "attr", "leader", "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +}; +const char *node_fields_kern[] = { + "attr", "kern", "expansion_factor", NULL +}; +const char *node_fields_penalty[] = { + "attr", "penalty", NULL +}; +const char *node_fields_unset[] = { + "attr", "width", "depth", "height", "dir", "shrink", "glue_order", + "glue_sign", "stretch", "span", "head", NULL +}; +const char *node_fields_margin_kern[] = { + "attr", "width", "glyph", NULL +}; +const char *node_fields_glyph[] = { + "attr", "char", "font", "lang", "left", "right", "uchyph", "components", + "xoffset", "yoffset", "width", "height", "depth", "expansion_factor", NULL +}; +const char *node_fields_inserting[] = { + "height", "last_ins_ptr", "best_ins_ptr", + "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +}; +const char *node_fields_splitup[] = { + "height", "last_ins_ptr", "best_ins_ptr", "broken_ptr", "broken_ins", NULL +}; +const char *node_fields_attribute[] = { + "number", "value", NULL +}; +const char *node_fields_glue_spec[] = { + "width", "stretch", "shrink", "stretch_order", "shrink_order", NULL +}; +const char *node_fields_attribute_list[] = { + NULL +}; +const char *node_fields_local_par[] = { + "attr", "pen_inter", "pen_broken", "dir", "box_left", "box_left_width", + "box_right", "box_right_width", NULL +}; +const char *node_fields_dir[] = { + "attr", "dir", "level", NULL +}; +const char *node_fields_boundary[] = { + "attr", "value", NULL +}; + +/* math nodes */ + +const char *node_fields_noad[] = { + "attr", "nucleus", "sub", "sup", NULL +}; + +const char *node_fields_style[] = { + "attr", "style", NULL +}; +const char *node_fields_choice[] = { + "attr", "display", "text", "script", "scriptscript", NULL +}; +const char *node_fields_radical[] = { + "attr", "nucleus", "sub", "sup", "left", "degree", "width", "options", NULL +}; +const char *node_fields_fraction[] = { + "attr", "width", "num", "denom", "left", "right", "middle", "options", NULL +}; +const char *node_fields_accent[] = { + "attr", "nucleus", "sub", "sup", "accent", "bot_accent", "top_accent", + "overlay_accent", "fraction", NULL +}; +const char *node_fields_fence[] = { + "attr", "delim", "italic", "height", "depth", "options", "class", NULL +}; +const char *node_fields_math_char[] = { + "attr", "fam", "char", NULL +}; +const char *node_fields_sub_box[] = { + "attr", "head", NULL +}; +const char *node_fields_sub_mlist[] = { + "attr", "head", NULL +}; +const char *node_fields_math_text_char[] = { + "attr", "fam", "char", NULL +}; +const char *node_fields_delim[] = { + "attr", "small_fam", "small_char", "large_fam", "large_char", NULL +}; + +/* whatsit nodes */ + +const char *node_fields_whatsit_open[] = { + "attr", "stream", "name", "area", "ext", NULL +}; +const char *node_fields_whatsit_write[] = { + "attr", "stream", "data", NULL +}; +const char *node_fields_whatsit_close[] = { + "attr", "stream", NULL +}; +const char *node_fields_whatsit_special[] = { + "attr", "data", NULL +}; +const char *node_fields_whatsit_save_pos[] = { + "attr", NULL +}; +const char *node_fields_whatsit_late_lua[] = { + "attr", "reg", "data", "name", "string", NULL +}; +const char *node_fields_whatsit_user_defined[] = { + "attr", "user_id", "type", "value", NULL +}; + +/* pdf backend whatsit nodes */ + +const char *node_fields_whatsit_pdf_literal[] = { + "attr", "mode", "data", NULL +}; +const char *node_fields_whatsit_pdf_refobj[] = { + "attr", "objnum", NULL +}; +const char *node_fields_whatsit_pdf_annot[] = { + "attr", "width", "depth", "height", "objnum", "data", NULL +}; +const char *node_fields_whatsit_pdf_start_link[] = { + "attr", "width", "depth", "height", "objnum", "link_attr", "action", NULL +}; +const char *node_fields_whatsit_pdf_end_link[] = { + "attr", NULL +}; +const char *node_fields_whatsit_pdf_dest[] = { + "attr", "width", "depth", "height", "named_id", "dest_id", "dest_type", + "xyz_zoom", "objnum", NULL +}; +const char *node_fields_whatsit_pdf_action[] = { + "action_type", "named_id", "action_id", "file", "new_window", "data", NULL +}; +const char *node_fields_whatsit_pdf_thread[] = { + "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL +}; +const char *node_fields_whatsit_pdf_start_thread[] = { + "attr", "width", "depth", "height", "named_id", "thread_id", "thread_attr", NULL +}; +const char *node_fields_whatsit_pdf_end_thread[] = { + "attr", NULL +}; +const char *node_fields_whatsit_pdf_colorstack[] = { + "attr", "stack", "cmd", "data", NULL +}; +const char *node_fields_whatsit_pdf_setmatrix[] = { + "attr", "data", NULL +}; +const char *node_fields_whatsit_pdf_save[] = { + "attr", NULL +}; +const char *node_fields_whatsit_pdf_restore[] = { + "attr", NULL +}; + +/* subtypes */ + +const char *node_subtypes_glue[] = { + "userskip", "lineskip", "baselineskip", "parskip", "abovedisplayskip", "belowdisplayskip", + "abovedisplayshortskip", "belowdisplayshortskip", "leftskip", "rightskip", "topskip", + "splittopskip", "tabskip", "spaceskip", "xspaceskip", "parfillskip", + "mathskip", "thinmuskip", "medmuskip", "thickmuskip", NULL +}; +const char *node_subtypes_mathglue[] = { /* 98+ */ + "conditionalmathskip", "muglue", NULL +}; +const char *node_subtypes_leader[] = { /* 100+ */ + "leaders", "cleaders", "xleaders", "gleaders", NULL +}; +const char *node_subtypes_fill[] = { + "stretch", "fi", "fil", "fill", "filll", NULL +}; +const char *node_subtypes_boundary[] = { + "cancel", "user", "protrusion", "word", NULL +}; +const char *node_subtypes_penalty[] = { + "userpenalty", "linebreakpenalty", "linepenalty", "wordpenalty", "finalpenalty", + "noadpenalty", "beforedisplaypenalty", "afterdisplaypenalty", "equationnumberpenalty", NULL +}; +const char *node_subtypes_kern[] = { + "fontkern", "userkern", "accentkern", "italiccorrection", NULL +}; +const char *node_subtypes_rule[] = { + "normal", "box", "image", "empty", "user", "over", "under", "fraction", "radical", NULL +}; +const char *node_subtypes_glyph[] = { + "character", "glyph", "ligature", "ghost", "left", "right", NULL +}; +const char *node_subtypes_disc[] = { + "discretionary", "explicit", "automatic", "regular", "first", "second", NULL +}; +const char *node_subtypes_marginkern[] = { + "left", "right", NULL +}; +const char *node_subtypes_list[] = { + "unknown", "line", "box", "indent", "alignment", "cell", "equation", "equationnumber", NULL +}; +const char *node_subtypes_adjust[] = { + "normal", "pre", NULL +}; +const char *node_subtypes_math[] = { + "beginmath", "endmath", NULL +}; +const char *node_subtypes_noad[] = { + "ord", "opdisplaylimits", "oplimits", "opnolimits", "bin", "rel", "open", "close", + "punct", "inner", "under", "over", "vcenter", NULL +}; +const char *node_subtypes_radical[] = { + "radical", "uradical", "uroot", "uunderdelimiter", "uoverdelimiter", "udelimiterunder", + "udelimiterover", NULL +}; +const char *node_subtypes_accent[] = { + "bothflexible", "fixedtop", "fixedbottom", "fixedboth", NULL, +}; +const char *node_subtypes_fence[] = { + "unset", "left", "middle", "right", NULL +}; + +node_info node_data[] = { /* the last entry in a row is the etex number */ + { hlist_node, box_node_size, node_fields_list, "hlist", 1 }, + { vlist_node, box_node_size, node_fields_list, "vlist", 2 }, + { rule_node, rule_node_size, node_fields_rule, "rule", 3 }, + { ins_node, ins_node_size, node_fields_insert, "ins", 4 }, + { mark_node, mark_node_size, node_fields_mark, "mark", 5 }, + { adjust_node, adjust_node_size, node_fields_adjust, "adjust", 6 }, + { boundary_node, boundary_node_size, node_fields_boundary, "boundary", -1 }, + { disc_node, disc_node_size, node_fields_disc, "disc", 8 }, + { whatsit_node, -1, NULL, "whatsit", 9 }, + { local_par_node, local_par_size, node_fields_local_par, "local_par", -1 }, + { dir_node, dir_node_size, node_fields_dir, "dir", -1 }, + { math_node, math_node_size, node_fields_math, "math", 10 }, + { glue_node, glue_node_size, node_fields_glue, "glue", 11 }, + { kern_node, kern_node_size, node_fields_kern, "kern", 12 }, + { penalty_node, penalty_node_size, node_fields_penalty, "penalty", 13 }, + { unset_node, box_node_size, node_fields_unset, "unset", 14 }, + { style_node, style_node_size, node_fields_style, "style", 15 }, + { choice_node, style_node_size, node_fields_choice, "choice", 15 }, + { simple_noad, noad_size, node_fields_noad, "noad", 15 }, + { radical_noad, radical_noad_size, node_fields_radical, "radical", 15 }, + { fraction_noad, fraction_noad_size, node_fields_fraction, "fraction", 15 }, + { accent_noad, accent_noad_size, node_fields_accent, "accent", 15 }, + { fence_noad, fence_noad_size, node_fields_fence, "fence", 15 }, + { math_char_node, math_kernel_node_size, node_fields_math_char, "math_char", 15 }, + { sub_box_node, math_kernel_node_size, node_fields_sub_box, "sub_box", 15 }, + { sub_mlist_node, math_kernel_node_size, node_fields_sub_mlist, "sub_mlist", 15 }, + { math_text_char_node, math_kernel_node_size, node_fields_math_text_char, "math_text_char", 15 }, + { delim_node, math_shield_node_size, node_fields_delim, "delim", 15 }, + { margin_kern_node, margin_kern_node_size, node_fields_margin_kern, "margin_kern", -1 }, + { glyph_node, glyph_node_size, node_fields_glyph, "glyph", 0 }, + { align_record_node, box_node_size, NULL, "align_record", -1 }, + { pseudo_file_node, pseudo_file_node_size, NULL, "pseudo_file", -1 }, + { pseudo_line_node, variable_node_size, NULL, "pseudo_line", -1 }, + { inserting_node, page_ins_node_size, node_fields_inserting, "page_insert", -1 }, + { split_up_node, page_ins_node_size, node_fields_splitup, "split_insert", -1 }, + { expr_node, expr_node_size, NULL, "expr_stack", -1 }, + { nesting_node, nesting_node_size, NULL, "nested_list", -1 }, + { span_node, span_node_size, NULL, "span", -1 }, + { attribute_node, attribute_node_size, node_fields_attribute, "attribute", -1 }, + { glue_spec_node, glue_spec_size, node_fields_glue_spec, "glue_spec", -1 }, + { attribute_list_node, attribute_node_size, node_fields_attribute_list, "attribute_list", -1 }, + { temp_node, temp_node_size, NULL, "temp", -1 }, + { align_stack_node, align_stack_node_size, NULL, "align_stack", -1 }, + { movement_node, movement_node_size, NULL, "movement_stack", -1 }, + { if_node, if_node_size, NULL, "if_stack", -1 }, + { unhyphenated_node, active_node_size, NULL, "unhyphenated", -1 }, + { hyphenated_node, active_node_size, NULL, "hyphenated", -1 }, + { delta_node, delta_node_size, NULL, "delta", -1 }, + { passive_node, passive_node_size, NULL, "passive", -1 }, + { shape_node, variable_node_size, NULL, "shape", -1 }, + { -1, -1, NULL, NULL, -1 }, +}; + +const char *node_subtypes_pdf_destination[] = { + "xyz", "fit", "fith", "fitv", "fitb", "fitbh", "fitbv", "fitr", NULL +}; +const char *node_subtypes_pdf_literal[] = { + "origin", "page", "direct", NULL +}; + +node_info whatsit_node_data[] = { + { open_node, open_node_size, node_fields_whatsit_open, "open", -1 }, + { write_node, write_node_size, node_fields_whatsit_write, "write", -1 }, + { close_node, close_node_size, node_fields_whatsit_close, "close", -1 }, + { special_node, special_node_size, node_fields_whatsit_special, "special", -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { save_pos_node, save_pos_node_size, node_fields_whatsit_save_pos, "save_pos", -1 }, + { late_lua_node, late_lua_node_size, node_fields_whatsit_late_lua, "late_lua", -1 }, + { user_defined_node, user_defined_node_size, node_fields_whatsit_user_defined, "user_defined", -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + { fake_node, fake_node_size, NULL, fake_node_name, -1 }, + /* here starts the dvi backend section, todo: a separate list */ + /* nothing for dvi */ + /* here starts the pdf backend section, todo: a separate list */ + { pdf_literal_node, write_node_size, node_fields_whatsit_pdf_literal, "pdf_literal", -1 }, + { pdf_refobj_node, pdf_refobj_node_size, node_fields_whatsit_pdf_refobj, "pdf_refobj", -1 }, + { pdf_annot_node, pdf_annot_node_size, node_fields_whatsit_pdf_annot, "pdf_annot", -1 }, + { pdf_start_link_node, pdf_annot_node_size, node_fields_whatsit_pdf_start_link, "pdf_start_link", -1 }, + { pdf_end_link_node, pdf_end_link_node_size, node_fields_whatsit_pdf_end_link, "pdf_end_link", -1 }, + { pdf_dest_node, pdf_dest_node_size, node_fields_whatsit_pdf_dest, "pdf_dest", -1 }, + { pdf_action_node, pdf_action_size, node_fields_whatsit_pdf_action, "pdf_action", -1 }, + { pdf_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_thread, "pdf_thread", -1 }, + { pdf_start_thread_node, pdf_thread_node_size, node_fields_whatsit_pdf_start_thread, "pdf_start_thread", -1 }, + { pdf_end_thread_node, pdf_end_thread_node_size, node_fields_whatsit_pdf_end_thread, "pdf_end_thread", -1 }, + { pdf_thread_data_node, pdf_thread_node_size, NULL, "pdf_thread_data", -1 }, + { pdf_link_data_node, pdf_annot_node_size, NULL, "pdf_link_data", -1 }, + { pdf_colorstack_node, pdf_colorstack_node_size, node_fields_whatsit_pdf_colorstack, "pdf_colorstack", -1 }, + { pdf_setmatrix_node, pdf_setmatrix_node_size, node_fields_whatsit_pdf_setmatrix, "pdf_setmatrix", -1 }, + { pdf_save_node, pdf_save_node_size, node_fields_whatsit_pdf_save, "pdf_save", -1 }, + { pdf_restore_node, pdf_restore_node_size, node_fields_whatsit_pdf_restore, "pdf_restore", -1 }, + /* done */ + { -1, -1, NULL, NULL, -1 }, +}; + +#define last_whatsit_node pdf_restore_node + +@ +When we copy a node list, there are several possibilities: we do the same as a new node, +we copy the entry to the table in properties (a reference), we do a deep copy of a table +in the properties, we create a new table and give it the original one as a metatable. +After some experiments (that also included timing) with these scenarios I decided that a +deep copy made no sense, nor did nilling. In the end both the shallow copy and the metatable +variant were both ok, although the second ons is slower. The most important aspect to keep +in mind is that references to other nodes in properties no longer can be valid for that +copy. We could use two tables (one unique and one shared) or metatables but that only +complicates matters. + +When defining a new node, we could already allocate a table but it is rather easy to do +that at the lua end e.g. using a metatable __index method. That way it is under macro +package control. + +When deleting a node, we could keep the slot (e.g. setting it to false) but it could make +memory consumption raise unneeded when we have temporary large node lists and after that +only small lists. + +So, in the end this is what we ended up with. For the record, I also experimented with the +following: + +- copy attributes to the properties so that we have fast access at the lua end: in the end + the overhead is not compensated by speed and convenience, in fact, attributes are not + that slow when it comes to accessing them + +- a bitset in the node but again the gain compared to attributes is neglectable and it also + demands a pretty string agreement over what bit represents what, and this is unlikely to + succeed in the tex community (I could use it for font handling, which is cross package, + but decided that it doesn't pay off + +In case one wonders why properties make sense then, well, it is not so much speed that we +gain, but more convenience: storing all kind of (temporary) data in attributes is no fun and +this mechanism makes sure that properties are cleaned up when a node is freed. Also, the +advantage of a more or less global properties table is that we stay at the lua end. An +alternative is to store a reference in the node itself but that is complicated by the fact +that the register has some limitations (no numeric keys) and we also don't want to mess with +it too much. + +@c +int lua_properties_level = 0 ; /* can be private */ +int lua_properties_enabled = 0 ; +int lua_properties_use_metatable = 0 ; + +@ +We keep track of nesting so that we don't oveflow the stack, and, what is more +important, don't keep resolving the registry index. + +@c +#define lua_properties_push do { \ + if (lua_properties_enabled) { \ + lua_properties_level = lua_properties_level + 1 ; \ + if (lua_properties_level == 1) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + } \ + } \ +} while(0) + +#define lua_properties_pop do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 1) \ + lua_pop(Luas,1); \ + lua_properties_level = lua_properties_level - 1 ; \ + } \ +} while(0) + +/* No setting is needed: */ + +#define lua_properties_set(target) do { \ +} while(0) + +/* Resetting boils down to nilling. */ + +#define lua_properties_reset(target) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_pushnil(Luas); \ + lua_rawseti(Luas,-2,target); \ + lua_pop(Luas,1); \ + } else { \ + lua_pushnil(Luas); \ + lua_rawseti(Luas,-2,target); \ + } \ + } \ +} while(0) + +/* + For a moment I considered supporting all kind of data types but in practice + that makes no sense. So we stick to a cheap shallow copy with as option a + metatable. Btw, a deep copy would look like this: + + static void copy_lua_table(lua_State* L, int index) { + lua_newtable(L); + lua_pushnil(L); + while(lua_next(L, index-1) != 0) { + lua_pushvalue(L, -2); + lua_insert(L, -2); + if (lua_type(L,-1)==LUA_TTABLE) + copy_lua_table(L,-1); + lua_settable(L, -4); + } + lua_pop(L,1); + } + + #define lua_properties_copy(target, source) do { \ + if (lua_properties_enabled) { \ + lua_pushinteger(Luas,source); \ + lua_rawget(Luas,-2); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + copy_lua_table(Luas,-1); \ + lua_pushinteger(Luas,target); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas,-3); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } while(0) + +*/ + +/* isn't there a faster way to metatable? */ + +/* + +#define lua_properties_copy(target,source) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setfield(Luas,-2,"__index"); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + lua_pop(Luas,1); \ + } else { \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setfield(Luas,-2,"__index"); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } \ +} while(0) + +*/ + +/* + A simple testrun on many pages of dumb text shows 1% gain (of course it depends + on how properties are used but some other tests confirm it). +*/ + +#define lua_properties_copy(target,source) do { \ + if (lua_properties_enabled) { \ + if (lua_properties_level == 0) { \ + lua_get_metatablelua_l(Luas,node_properties); \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_push_string_by_name(Luas,__index); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas, -3); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + lua_pop(Luas,1); \ + } else { \ + lua_rawgeti(Luas,-1,source); \ + if (lua_type(Luas,-1)==LUA_TTABLE) { \ + if (lua_properties_use_metatable) { \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_push_string_by_name(Luas,__index); \ + lua_insert(Luas,-2); \ + lua_rawset(Luas, -3); \ + lua_newtable(Luas); \ + lua_insert(Luas,-2); \ + lua_setmetatable(Luas,-2); \ + } \ + lua_rawseti(Luas,-2,target); \ + } else { \ + lua_pop(Luas,1); \ + } \ + } \ + } \ +} while(0) + +/* Here end the property handlers. */ + +@ @c +int valid_node(halfword p) +{ + if (p > my_prealloc && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + if (varmem_sizes[p] > 0) { + return 1; + } +#else + return 1; +#endif + } + return 0; +} + +@ @c +static int test_count = 1; + +#define dorangetest(a,b,c) do { \ + if (!(b>=0 && b my_prealloc && varmem_sizes[s] == 0) { + s--; + } + if (s != null + && s != my_prealloc + && s != var_mem_max + && (r - s) < get_node_size(type(s), subtype(s)) + && alink(s) != p) { + if (type(s) == disc_node) { + fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + fprintf(stdout, "pre_break(%d,%d,%d), ", + (int) vlink_pre_break(s), (int) tlink(pre_break(s)), + (int) alink(pre_break(s))); + fprintf(stdout, "post_break(%d,%d,%d), ", + (int) vlink_post_break(s), + (int) tlink(post_break(s)), + (int) alink(post_break(s))); + fprintf(stdout, "no_break(%d,%d,%d)", + (int) vlink_no_break(s), (int) tlink(no_break(s)), + (int) alink(no_break(s))); + fprintf(stdout, "\n"); + } else { + if (vlink(s) == p + || (type(s) == glyph_node && lig_ptr (s) == p) + || (type(s) == vlist_node && list_ptr(s) == p) + || (type(s) == hlist_node && list_ptr(s) == p) + || (type(s) == unset_node && list_ptr(s) == p) + || (type(s) == ins_node && ins_ptr (s) == p) + ) { + fprintf(stdout," pointed to from %s node %d (vlink %d, alink %d): ", + get_node_name(type(s), subtype(s)), (int) s, + (int) vlink(s), (int) alink(s)); + if (type(s) == glyph_node) { + fprintf(stdout, "lig_ptr(%d)", (int) lig_ptr(s)); + } else if (type(s) == vlist_node || type(s) == hlist_node) { + fprintf(stdout, "list_ptr(%d)", (int) list_ptr(s)); + } + fprintf(stdout, "\n"); + } else { + if ((type(s) != penalty_node) && (type(s) != math_node) && (type(s) != kern_node)) { + fprintf(stdout, " pointed to from %s node %d\n", + get_node_name(type(s), subtype(s)), (int) s); + } + } + } + } + } + } +} + +#endif + +static int free_error(halfword p) +{ + if (p > my_prealloc && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + int i; + if (varmem_sizes[p] == 0) { + check_static_node_mem(); + for (i = (my_prealloc + 1); i < var_mem_max; i++) { + if (varmem_sizes[i] > 0) { + check_node(i); + } + } + test_count++; + if (type(p) == glyph_node) { + formatted_error("nodes", "attempt to double-free glyph (%c) node %d, ignored", (int) character(p), (int) p); + } else { + formatted_error("nodes", "attempt to double-free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); + } + node_mem_dump(p); + return 1; + } +#endif + } else { + formatted_error("nodes", "attempt to free an impossible node %d", (int) p); + return 1; + } + return 0; +} + +@ @c +static int copy_error(halfword p) +{ + if (p >= 0 && p < var_mem_max) { +#ifdef CHECK_NODE_USAGE + if (p > my_prealloc && varmem_sizes[p] == 0) { + if (type(p) == glyph_node) { + formatted_warning("nodes", "attempt to copy free glyph (%c) node %d, ignored", (int) character(p), (int) p); + } else { + formatted_warning("nodes", "attempt to copy free %s node %d, ignored", get_node_name(type(p), subtype(p)), (int) p); + } + return 1; + } +#endif + } else { + formatted_error("nodes", "attempt to copy an impossible node %d", (int) p); + return 1; + } + return 0; +} + +@ @c +halfword new_node(int i, int j) +{ + int s = get_node_size(i, j); + halfword n = get_node(s); + /* + It should be possible to do this memset at |free_node()|. + + Both type() and subtype() will be set below, and vlink() is + set to null by |get_node()|, so we can do we clearing one + word less than |s| + */ + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) s - 1))); + switch (i) { + case glyph_node: + init_lang_data(n); + break; + case hlist_node: + case vlist_node: + box_dir(n) = -1; + break; + case disc_node: + pre_break(n) = pre_break_head(n); + type(pre_break(n)) = nesting_node; + subtype(pre_break(n)) = pre_break_head(0); + post_break(n) = post_break_head(n); + type(post_break(n)) = nesting_node; + subtype(post_break(n)) = post_break_head(0); + no_break(n) = no_break_head(n); + type(no_break(n)) = nesting_node; + subtype(no_break(n)) = no_break_head(0); + break; + case rule_node: + depth(n) = null_flag; + height(n) = null_flag; + width(n) = null_flag; + rule_dir(n) = -1; + rule_index(n) = 0; + rule_transform(n) = 0; + break; + case whatsit_node: + if (j == open_node) { + open_name(n) = get_nullstr(); + open_area(n) = open_name(n); + open_ext(n) = open_name(n); + } + break; + case unset_node: + width(n) = null_flag; + break; + case pseudo_line_node: + case shape_node: + /* this is a trick that makes |pseudo_files| slightly slower, + but the overall allocation faster then an explicit test + at the top of |new_node()|. + */ + if (j>0) { + free_node(n, variable_node_size); + n = slow_get_node(j); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * ((unsigned) j - 1))); + } + break; + default: + break; + } + if (synctex_par) { + /* handle synctex extension */ + switch (i) { + case math_node: + synctex_tag_math(n) = cur_input.synctex_tag_field; + synctex_line_math(n) = line; + break; + case glue_node: + synctex_tag_glue(n) = cur_input.synctex_tag_field; + synctex_line_glue(n) = line; + break; + case kern_node: + if (j != 0) { + synctex_tag_kern(n) = cur_input.synctex_tag_field; + synctex_line_kern(n) = line; + } + break; + case hlist_node: + case vlist_node: + case unset_node: + synctex_tag_box(n) = cur_input.synctex_tag_field; + synctex_line_box(n) = line; + break; + case rule_node: + synctex_tag_rule(n) = cur_input.synctex_tag_field; + synctex_line_rule(n) = line; + break; + } + } + /* take care of attributes */ + if (nodetype_has_attributes(i)) { + build_attribute_list(n); + /* lua_properties_set */ + } + type(n) = (quarterword) i; + subtype(n) = (quarterword) j; + return n; +} + +halfword raw_glyph_node(void) +{ + register halfword n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); + type(n) = glyph_node; + subtype(n) = 0; + return n; +} + +halfword new_glyph_node(void) +{ + register halfword n = get_node(glyph_node_size); + (void) memset((void *) (varmem + n + 1), 0, (sizeof(memory_word) * (glyph_node_size - 1))); + type(n) = glyph_node; + subtype(n) = 0; + build_attribute_list(n); + /* lua_properties_set */ + return n; +} + +@ makes a duplicate of the node list that starts at |p| and returns a +pointer to the new list + +@c +halfword do_copy_node_list(halfword p, halfword end) +{ + halfword q = null; /* previous position in new list */ + halfword h = null; /* head of the list */ + register halfword s ; + lua_properties_push; /* saves stack and time */ + while (p != end) { + s = copy_node(p); + if (h == null) { + h = s; + } else { + couple_nodes(q, s); + } + q = s; + p = vlink(p); + } + lua_properties_pop; /* saves stack and time */ + return h; +} + +halfword copy_node_list(halfword p) +{ + return do_copy_node_list(p, null); +} + +#define copy_sub_list(target,source) do { \ + if (source != null) { \ + s = do_copy_node_list(source, null); \ + target = s; \ + } else { \ + target = null; \ + } \ + } while (0) + +#define copy_sub_node(target,source) do { \ + if (source != null) { \ + s = copy_node(source); \ + target = s ; \ + } else { \ + target = null; \ + } \ +} while (0) + +@ make a dupe of a single node + +@c +static void copy_node_wrapup_core(halfword p, halfword r) +{ + halfword s ; + switch (subtype(p)) { + case write_node: + case special_node: + add_token_ref(write_tokens(p)); + break; + case late_lua_node: + copy_late_lua(r, p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + add_node_attr_ref(user_node_value(p)); + break; + case 'l': + copy_user_lua(r, p); + break; + case 'n': + s = copy_node_list(user_node_value(p)); + user_node_value(r) = s; + break; + case 's': + /* |add_string_ref(user_node_value(p));| */ + break; + case 't': + add_token_ref(user_node_value(p)); + break; + } + break; + default: + break ; + } +} + +void copy_node_wrapup_dvi(halfword p, halfword r) +{ +} + +void copy_node_wrapup_pdf(halfword p, halfword r) +{ + switch(subtype(p)) { + case pdf_literal_node: + copy_pdf_literal(r, p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + add_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + add_token_ref(pdf_setmatrix_data(p)); + break; + case pdf_annot_node: + add_token_ref(pdf_annot_data(p)); + break; + case pdf_start_link_node: + if (pdf_link_attr(r) != null) + add_token_ref(pdf_link_attr(r)); + add_action_ref(pdf_link_action(r)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + add_token_ref(pdf_dest_id(p)); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + add_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + add_token_ref(pdf_thread_attr(p)); + break; + default: + break; + } +} + +halfword copy_node(const halfword p) +{ + halfword r; /* current node being fabricated for new list */ + halfword w; /* whatsit subtype */ + halfword t; /* type of node */ + register halfword s; /* a helper variable for copying into variable mem */ + register int i; + if (copy_error(p)) { + r = new_node(temp_node, 0); + return r; + } + t = type(p); + i = get_node_size(t,subtype(p)); + r = get_node(i); + + (void) memcpy((void *) (varmem + r), (void *) (varmem + p), (sizeof(memory_word) * (unsigned) i)); + + /* possible speedup: */ + /* + if t == glue_spec) { + return r; + } + */ + + if (synctex_par) { + /* handle synctex extension */ + switch (t) { + case math_node: + synctex_tag_math(r) = cur_input.synctex_tag_field; + synctex_line_math(r) = line; + break; + case kern_node: + synctex_tag_kern(r) = cur_input.synctex_tag_field; + synctex_line_kern(r) = line; + break; + } + } + if (nodetype_has_attributes(t)) { + add_node_attr_ref(node_attr(p)); + alink(r) = null; + lua_properties_copy(r,p); + } + vlink(r) = null; + + switch (t) { + case glyph_node: + copy_sub_list(lig_ptr(r),lig_ptr(p)) ; + break; + case glue_node: + copy_sub_list(leader_ptr(r),leader_ptr(p)) ; + break; + case hlist_node: + case vlist_node: + case unset_node: + copy_sub_list(list_ptr(r),list_ptr(p)) ; + break; + case disc_node: + pre_break(r) = pre_break_head(r); + if (vlink_pre_break(p) != null) { + s = copy_node_list(vlink_pre_break(p)); + alink(s) = pre_break(r); + tlink_pre_break(r) = tail_of_list(s); + vlink_pre_break(r) = s; + } else { + assert(tlink(pre_break(r)) == null); + } + post_break(r) = post_break_head(r); + if (vlink_post_break(p) != null) { + s = copy_node_list(vlink_post_break(p)); + alink(s) = post_break(r); + tlink_post_break(r) = tail_of_list(s); + vlink_post_break(r) = s; + } else { + assert(tlink_post_break(r) == null); + } + no_break(r) = no_break_head(r); + if (vlink(no_break(p)) != null) { + s = copy_node_list(vlink_no_break(p)); + alink(s) = no_break(r); + tlink_no_break(r) = tail_of_list(s); + vlink_no_break(r) = s; + } else { + assert(tlink_no_break(r) == null); + } + break; + case math_node: + break; + case ins_node: + copy_sub_list(ins_ptr(r),ins_ptr(p)) ; + break; + case margin_kern_node: + copy_sub_node(margin_char(r),margin_char(p)); + break; + case mark_node: + add_token_ref(mark_ptr(p)); + break; + case adjust_node: + copy_sub_list(adjust_ptr(r),adjust_ptr(p)); + break; + case choice_node: + copy_sub_list(display_mlist(r),display_mlist(p)) ; + copy_sub_list(text_mlist(r),text_mlist(p)) ; + copy_sub_list(script_mlist(r),script_mlist(p)) ; + copy_sub_list(script_script_mlist(r),script_script_mlist(p)) ; + break; + case simple_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + break; + case radical_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + copy_sub_node(left_delimiter(r),left_delimiter(p)) ; + copy_sub_list(degree(r),degree(p)) ; + break; + case accent_noad: + copy_sub_list(nucleus(r),nucleus(p)) ; + copy_sub_list(subscr(r),subscr(p)) ; + copy_sub_list(supscr(r),supscr(p)) ; + copy_sub_list(top_accent_chr(r),top_accent_chr(p)) ; + copy_sub_list(bot_accent_chr(r),bot_accent_chr(p)) ; + copy_sub_list(overlay_accent_chr(r),overlay_accent_chr(p)) ; + break; + case fence_noad: + copy_sub_node(delimiter(r),delimiter(p)) ; + break; + case sub_box_node: + case sub_mlist_node: + copy_sub_list(math_list(r),math_list(p)) ; + break; + case fraction_noad: + copy_sub_list(numerator(r),numerator(p)) ; + copy_sub_list(denominator(r),denominator(p)) ; + copy_sub_node(left_delimiter(r),left_delimiter(p)) ; + copy_sub_node(right_delimiter(r),right_delimiter(p)) ; + break; + case glue_spec_node: + case dir_node: + case local_par_node: + case boundary_node: + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + copy_node_wrapup_pdf(p,r); + } else if (w >= backend_first_dvi_whatsit) { + copy_node_wrapup_dvi(p,r); + } else { + copy_node_wrapup_core(p,r); + } + break; + } + return r; +} + +/* x */ + +#define free_sub_list(source) if (source != null) flush_node_list(source); +#define free_sub_node(source) if (source != null) flush_node(source); + +@ @c + +static void flush_node_wrapup_core(halfword p) +{ + switch (subtype(p)) { + case open_node: + case write_node: + case close_node: + case save_pos_node: + break; + case special_node: + delete_token_ref(write_tokens(p)); + break; + case late_lua_node: + free_late_lua(p); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + delete_attribute_ref(user_node_value(p)); + break; + case 'd': + break; + case 'l': + free_user_lua(user_node_value(p)); + break; + case 'n': + flush_node_list(user_node_value(p)); + break; + case 's': + /* |delete_string_ref(user_node_value(p));| *//* if this was mpost .. */ + break; + case 't': + delete_token_ref(user_node_value(p)); + break; + default: + { + const char *hlp[] = { + "The type of the value in a user defined whatsit node should be one", + "of 'a' (attribute list), 'd' (number), 'n' (node list), 's' (string),", + "or 't' (tokenlist). Yours has an unknown type, and therefore I don't", + "know how to free the node's value. A memory leak may result.", + NULL + }; + tex_error("Unidentified user defined whatsit", hlp); + } + break; + } + break; + } +} + +void flush_node_wrapup_dvi(halfword p) +{ +} + +void flush_node_wrapup_pdf(halfword p) +{ + switch(subtype(p)) { + case pdf_save_node: + case pdf_restore_node: + case pdf_refobj_node: + case pdf_end_link_node: + case pdf_end_thread_node: + break; + case pdf_literal_node: + free_pdf_literal(p); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + delete_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + delete_token_ref(pdf_setmatrix_data(p)); + break; + case pdf_annot_node: + delete_token_ref(pdf_annot_data(p)); + break; + case pdf_link_data_node: + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + delete_token_ref(pdf_link_attr(p)); + delete_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + delete_token_ref(pdf_dest_id(p)); + break; + case pdf_action_node: + if (pdf_action_type(p) == pdf_action_user) { + delete_token_ref(pdf_action_tokens(p)); + } else { + if (pdf_action_file(p) != null) + delete_token_ref(pdf_action_file(p)); + if (pdf_action_type(p) == pdf_action_page) + delete_token_ref(pdf_action_tokens(p)); + else if (pdf_action_named_id(p) > 0) + delete_token_ref(pdf_action_id(p)); + } + break; + case pdf_thread_data_node: + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + delete_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + delete_token_ref(pdf_thread_attr(p)); + break; + } +} + +void flush_node(halfword p) +{ + halfword w; + if (p == null) /* legal, but no-op */ + return; + if (free_error(p)) + return; + switch (type(p)) { + case glyph_node: + free_sub_list(lig_ptr(p)); + break; + case glue_node: + free_sub_list(leader_ptr(p)); + break; + case hlist_node: + case vlist_node: + case unset_node: + free_sub_list(list_ptr(p)); + break; + case disc_node: + /* watch the start at temp node hack */ + free_sub_list(vlink(pre_break(p))); + free_sub_list(vlink(post_break(p))); + free_sub_list(vlink(no_break(p))); + break; + case rule_node: + case kern_node: + case penalty_node: + case math_node: + break; + case glue_spec_node: + /* this allows free-ing of lua-allocated glue specs */ +//if (valid_node(p)) { +// free_node(p, subtype(p)); +//} +// return ; + break ; + case dir_node: + case local_par_node: + case boundary_node: + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + flush_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + flush_node_wrapup_dvi(p); + } else { + flush_node_wrapup_core(p); + } + break; + case ins_node: + flush_node_list(ins_ptr(p)); + break; + case margin_kern_node: + flush_node(margin_char(p)); + break; + case mark_node: + delete_token_ref(mark_ptr(p)); + break; + case adjust_node: + flush_node_list(adjust_ptr(p)); + break; + case style_node: /* nothing to do */ + break; + case choice_node: + free_sub_list(display_mlist(p)); + free_sub_list(text_mlist(p)); + free_sub_list(script_mlist(p)); + free_sub_list(script_script_mlist(p)); + break; + case simple_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + break; + case radical_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + free_sub_node(left_delimiter(p)); + free_sub_list(degree(p)); + break; + case accent_noad: + free_sub_list(nucleus(p)); + free_sub_list(subscr(p)); + free_sub_list(supscr(p)); + free_sub_list(top_accent_chr(p)); + free_sub_list(bot_accent_chr(p)); + free_sub_list(overlay_accent_chr(p)); + break; + case fence_noad: + free_sub_list(delimiter(p)); + break; + case delim_node: /* nothing to do */ + case math_char_node: + case math_text_char_node: + break; + case sub_box_node: + case sub_mlist_node: + free_sub_list(math_list(p)); + break; + case fraction_noad: + free_sub_list(numerator(p)); + free_sub_list(denominator(p)); + free_sub_node(left_delimiter(p)); + free_sub_node(right_delimiter(p)); + break; + case pseudo_file_node: + free_sub_list(pseudo_lines(p)); + break; + case pseudo_line_node: + case shape_node: + free_node(p, subtype(p)); + return; + break; + case align_stack_node: + case span_node: + case movement_node: + case if_node: + case nesting_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case inserting_node: + case split_up_node: + case expr_node: + case attribute_node: + case attribute_list_node: + case temp_node: + break; + default: + formatted_error("nodes","flushing weird node type %d", type(p)); + return; + } + if (nodetype_has_attributes(type(p))) { + delete_attribute_ref(node_attr(p)); + lua_properties_reset(p); + } + free_node(p, get_node_size(type(p), subtype(p))); + return; +} + +@ @c +void flush_node_list(halfword pp) +{ /* erase list of nodes starting at |p| */ + register halfword p = pp; + if (p == null) /* legal, but no-op */ + return; + if (free_error(p)) + return; + lua_properties_push; /* saves stack and time */ + while (p != null) { + register halfword q = vlink(p); + flush_node(p); + p = q; + } + lua_properties_pop; /* saves stack and time */ +} + +@ @c +static void check_node_wrapup_core(halfword p) +{ + switch (subtype(p)) { + /* frontend code */ + case special_node: + check_token_ref(write_tokens(p)); + break; + case user_defined_node: + switch (user_node_type(p)) { + case 'a': + check_attribute_ref(user_node_value(p)); + break; + case 't': + check_token_ref(user_node_value(p)); + break; + case 'n': + dorangetest(p, user_node_value(p), var_mem_max); + break; + case 's': + case 'd': + break; + default: + confusion("unknown user node type"); + break; + } + break; + case open_node: + case write_node: + case close_node: + case save_pos_node: + break; + } +} + +void check_node_wrapup_dvi(halfword p) +{ +} + +void check_node_wrapup_pdf(halfword p) +{ + switch (subtype(p)) { + case pdf_literal_node: + if (pdf_literal_type(p) == normal) + check_token_ref(pdf_literal_data(p)); + break; + case pdf_colorstack_node: + if (pdf_colorstack_cmd(p) <= colorstack_data) + check_token_ref(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + check_token_ref(pdf_setmatrix_data(p)); + break; + case late_lua_node: + if (late_lua_name(p) > 0) + check_token_ref(late_lua_name(p)); + if (late_lua_type(p) == normal) + check_token_ref(late_lua_data(p)); + break; + case pdf_annot_node: + check_token_ref(pdf_annot_data(p)); + break; + case pdf_start_link_node: + if (pdf_link_attr(p) != null) + check_token_ref(pdf_link_attr(p)); + check_action_ref(pdf_link_action(p)); + break; + case pdf_dest_node: + if (pdf_dest_named_id(p) > 0) + check_token_ref(pdf_dest_id(p)); + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (pdf_thread_named_id(p) > 0) + check_token_ref(pdf_thread_id(p)); + if (pdf_thread_attr(p) != null) + check_token_ref(pdf_thread_attr(p)); + break; + case pdf_save_node: + case pdf_restore_node: + case pdf_refobj_node: + case pdf_end_link_node: + case pdf_end_thread_node: + break; + default: + confusion("wrapup pdf nodes"); + break; + } +} + +void check_node(halfword p) +{ + halfword w ; + switch (type(p)) { + case glyph_node: + dorangetest(p, lig_ptr(p), var_mem_max); + break; + case glue_node: + dorangetest(p, leader_ptr(p), var_mem_max); + break; + case hlist_node: + case vlist_node: + case unset_node: + case align_record_node: + dorangetest(p, list_ptr(p), var_mem_max); + break; + case ins_node: + dorangetest(p, ins_ptr(p), var_mem_max); + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + check_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + check_node_wrapup_dvi(p); + } else { + check_node_wrapup_core(p); + } + break; + case margin_kern_node: + check_node(margin_char(p)); + break; + case math_node: + break; + case disc_node: + dorangetest(p, vlink(pre_break(p)), var_mem_max); + dorangetest(p, vlink(post_break(p)), var_mem_max); + dorangetest(p, vlink(no_break(p)), var_mem_max); + break; + case adjust_node: + dorangetest(p, adjust_ptr(p), var_mem_max); + break; + case pseudo_file_node: + dorangetest(p, pseudo_lines(p), var_mem_max); + break; + case pseudo_line_node: + case shape_node: + break; + case choice_node: + dorangetest(p, display_mlist(p), var_mem_max); + dorangetest(p, text_mlist(p), var_mem_max); + dorangetest(p, script_mlist(p), var_mem_max); + dorangetest(p, script_script_mlist(p), var_mem_max); + break; + case fraction_noad: + dorangetest(p, numerator(p), var_mem_max); + dorangetest(p, denominator(p), var_mem_max); + dorangetest(p, left_delimiter(p), var_mem_max); + dorangetest(p, right_delimiter(p), var_mem_max); + break; + case simple_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + break; + case radical_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, degree(p), var_mem_max); + dorangetest(p, left_delimiter(p), var_mem_max); + break; + case accent_noad: + dorangetest(p, nucleus(p), var_mem_max); + dorangetest(p, subscr(p), var_mem_max); + dorangetest(p, supscr(p), var_mem_max); + dorangetest(p, top_accent_chr(p), var_mem_max); + dorangetest(p, bot_accent_chr(p), var_mem_max); + dorangetest(p, overlay_accent_chr(p), var_mem_max); + break; + case fence_noad: + dorangetest(p, delimiter(p), var_mem_max); + break; + /* + case rule_node: + case kern_node: + case penalty_node: + case mark_node: + case style_node: + case attribute_list_node: + case attribute_node: + case glue_spec_node: + case temp_node: + case align_stack_node: + case movement_node: + case if_node: + case nesting_node: + case span_node: + case unhyphenated_node: + case hyphenated_node: + case delta_node: + case passive_node: + case expr_node: + case dir_node: + case boundary_node: + case local_par_node: + break; + default: + fprintf(stdout, "check_node: type is %d\n", type(p)); + */ + } +} + +@ @c +void fix_node_list(halfword head) +{ + halfword p, q; + if (head == null) + return; + p = head; + q = vlink(p); + while (q != null) { + alink(q) = p; + p = q; + q = vlink(p); + } +} + +@ @c +halfword get_node(int s) +{ + register halfword r; + + if (s < MAX_CHAIN_SIZE) { + r = free_chain[s]; + if (r != null) { + free_chain[s] = vlink(r); +#ifdef CHECK_NODE_USAGE + varmem_sizes[r] = (char) s; +#endif + vlink(r) = null; + var_used += s; /* maintain usage statistics */ + return r; + } + /* this is the end of the 'inner loop' */ + return slow_get_node(s); + } else { + normal_error("nodes","there is a problem in getting a node, case 1"); + return null; + } +} + +@ @c +void free_node(halfword p, int s) +{ + if (p <= my_prealloc) { + formatted_error("nodes", "node number %d of type %d should not be freed", (int) p, type(p)); + return; + } +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + if (s < MAX_CHAIN_SIZE) { + vlink(p) = free_chain[s]; + free_chain[s] = p; + } else { + /* todo ? it is perhaps possible to merge this node with an existing rover */ + node_size(p) = s; + vlink(p) = rover; + while (vlink(rover) != vlink(p)) { + rover = vlink(rover); + } + vlink(rover) = p; + } + /* maintain statistics */ + var_used -= s; +} + +@ @c +static void free_node_chain(halfword q, int s) +{ + register halfword p = q; + while (vlink(p) != null) { +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + var_used -= s; + p = vlink(p); + } + var_used -= s; +#ifdef CHECK_NODE_USAGE + varmem_sizes[p] = 0; +#endif + vlink(p) = free_chain[s]; + free_chain[s] = q; +} + +@ At the start of the node memory area we reserve some special nodes, +for instance frequently used glue specifications. We could as well just +use new_glue here but for the moment we stick to the traditional approach. + +@c +#define initialize_glue(n,wi,st,sh,sto,sho) \ + vlink(n) = null; \ + type(n) = glue_spec_node; \ + width(n) = wi; \ + stretch(n) = st; \ + shrink(n) = sh; \ + stretch_order(n) = sto; \ + shrink_order(n) = sho; + +#define initialize_whatever(n,t) \ + vinfo(n) = 0; \ + type(n) = t; \ + vlink(n) = null; \ + alink(n) = null; + +#define initialize_point(n) \ + type(n) = glyph_node; \ + subtype(n) = 0; \ + vlink(n) = null; \ + vinfo(n + 1) = null; \ + alink(n) = null; \ + font(n) = 0; \ + character(n) = '.'; \ + vinfo(n + 3) = 0; \ + vlink(n + 3) = 0; \ + vinfo(n + 4) = 0; \ + vlink(n + 4) = 0; + +void init_node_mem(int t) +{ + my_prealloc = var_mem_stat_max; + + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) t); + if (varmem == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem), 0, (unsigned) t * sizeof(memory_word)); +#ifdef CHECK_NODE_USAGE + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) t); + if (varmem_sizes == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) varmem_sizes, 0, sizeof(char) * (unsigned) t); +#endif + var_mem_max = t; + rover = var_mem_stat_max + 1; + vlink(rover) = rover; + node_size(rover) = (t - rover); + var_used = 0; + + /* initialize static glue specs */ + + initialize_glue(zero_glue,0,0,0,0,0); + initialize_glue(sfi_glue,0,0,0,sfi,0); + initialize_glue(fil_glue,0,unity,0,fil,0); + initialize_glue(fill_glue,0,unity,0,fill,0); + initialize_glue(ss_glue,0,unity,unity,fil,fil); + initialize_glue(fil_neg_glue,0,-unity,0,fil,0); + + /* initialize node list heads */ + + initialize_whatever(page_ins_head,temp_node); + initialize_whatever(contrib_head,temp_node); + initialize_whatever(page_head,temp_node); + initialize_whatever(temp_head,temp_node); + initialize_whatever(hold_head,temp_node); + initialize_whatever(adjust_head,temp_node); + initialize_whatever(pre_adjust_head,temp_node); + initialize_whatever(align_head,temp_node); + + initialize_whatever(active,unhyphenated_node); + initialize_whatever(end_span,span_node); + + initialize_point(begin_point); + initialize_point(end_point); +} + +@ @c +void dump_node_mem(void) +{ + dump_int(var_mem_max); + dump_int(rover); + dump_things(varmem[0], var_mem_max); +#ifdef CHECK_NODE_USAGE + dump_things(varmem_sizes[0], var_mem_max); +#endif + dump_things(free_chain[0], MAX_CHAIN_SIZE); + dump_int(var_used); + dump_int(my_prealloc); +} + +@ it makes sense to enlarge the varmem array immediately +@c + +void undump_node_mem(void) +{ + int x; + undump_int(x); + undump_int(rover); + var_mem_max = (x < 100000 ? 100000 : x); + varmem = xmallocarray(memory_word, (unsigned) var_mem_max); + undump_things(varmem[0], x); +#ifdef CHECK_NODE_USAGE + varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memset((void *) varmem_sizes, 0, (unsigned) var_mem_max * sizeof(char)); + undump_things(varmem_sizes[0], x); +#endif + undump_things(free_chain[0], MAX_CHAIN_SIZE); + undump_int(var_used); + undump_int(my_prealloc); + if (var_mem_max > x) { + /* todo ? it is perhaps possible to merge the new node with an existing rover */ + vlink(x) = rover; + node_size(x) = (var_mem_max - x); + while (vlink(rover) != vlink(x)) { + rover = vlink(rover); + } + vlink(rover) = x; + } +} + +@ @c +halfword slow_get_node(int s) +{ + register int t; + + RETRY: + t = node_size(rover); + if (vlink(rover) < var_mem_max && vlink(rover) != 0) { + if (t > s) { + /* allocating from the bottom helps decrease page faults */ + register halfword r = rover; + rover += s; + vlink(rover) = vlink(r); + node_size(rover) = node_size(r) - s; + if (vlink(rover) != r) { /* list is longer than one */ + halfword q = r; + while (vlink(q) != r) { + q = vlink(q); + } + vlink(q) += s; + } else { + vlink(rover) += s; + } + if (vlink(rover) < var_mem_max) { +#ifdef CHECK_NODE_USAGE + varmem_sizes[r] = (char) (s > 127 ? 127 : s); +#endif + vlink(r) = null; + var_used += s; /* maintain usage statistics */ + return r; /* this is the only exit */ + } else { + normal_error("nodes","there is a problem in getting a node, case 2"); + return null; + } + } else { + /* attempt to keep the free list small */ + int x; + if (vlink(rover) != rover) { + if (t < MAX_CHAIN_SIZE) { + halfword l = vlink(rover); + vlink(rover) = free_chain[t]; + free_chain[t] = rover; + rover = l; + while (vlink(l) != free_chain[t]) { + l = vlink(l); + } + vlink(l) = rover; + goto RETRY; + } else { + halfword l = rover; + while (vlink(rover) != l) { + if (node_size(rover) > s) { + goto RETRY; + } + rover = vlink(rover); + } + } + } + /* if we are still here, it was apparently impossible to get a match */ + x = (var_mem_max >> 2) + s; + varmem = (memory_word *) realloc((void *) varmem, sizeof(memory_word) * (unsigned) (var_mem_max + x)); + if (varmem == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem + var_mem_max), 0, (unsigned) x * sizeof(memory_word)); +#ifdef CHECK_NODE_USAGE + varmem_sizes = (char *) realloc(varmem_sizes, sizeof(char) * (unsigned) (var_mem_max + x)); + if (varmem_sizes == NULL) { + overflow("node memory size", (unsigned) var_mem_max); + } + memset((void *) (varmem_sizes + var_mem_max), 0, (unsigned) (x) * sizeof(char)); +#endif + /* todo ? it is perhaps possible to merge the new memory with an existing rover */ + vlink(var_mem_max) = rover; + node_size(var_mem_max) = x; + while (vlink(rover) != vlink(var_mem_max)) { + rover = vlink(rover); + } + vlink(rover) = var_mem_max; + rover = var_mem_max; + var_mem_max += x; + goto RETRY; + } + } else { + normal_error("nodes","there is a problem in getting a node, case 3"); + return null; + } +} + +@ @c +char *sprint_node_mem_usage(void) +{ + char *s; +#ifdef CHECK_NODE_USAGE + char *ss; + int i; + int b = 0; + char msg[256]; + int node_counts[last_normal_node + last_whatsit_node + 2] = { 0 }; + s = strdup(""); + for (i = (var_mem_max - 1); i > my_prealloc; i--) { + if (varmem_sizes[i] > 0) { + if (type(i) > last_normal_node + last_whatsit_node) { + node_counts[last_normal_node + last_whatsit_node + 1]++; + } else if (type(i) == whatsit_node) { + node_counts[(subtype(i) + last_normal_node + 1)]++; + } else { + node_counts[type(i)]++; + } + } + } + for (i = 0; i < last_normal_node + last_whatsit_node + 2; i++) { + if (node_counts[i] > 0) { + int j = + (i > (last_normal_node + 1) ? (i - last_normal_node - 1) : 0); + snprintf(msg, 255, "%s%d %s", (b ? ", " : ""), (int) node_counts[i], + get_node_name((i > last_normal_node ? whatsit_node : i), j)); + ss = xmalloc((unsigned) (strlen(s) + strlen(msg) + 1)); + strcpy(ss, s); + strcat(ss, msg); + free(s); + s = ss; + b = 1; + } + } +#else + s = strdup(""); +#endif + return s; +} + +@ @c +halfword list_node_mem_usage(void) +{ + halfword q = null; +#ifdef CHECK_NODE_USAGE + halfword p = null; + halfword i, j; + char *saved_varmem_sizes = xmallocarray(char, (unsigned) var_mem_max); + memcpy(saved_varmem_sizes, varmem_sizes, (size_t) var_mem_max); + for (i = my_prealloc + 1; i < (var_mem_max - 1); i++) { + if (saved_varmem_sizes[i] > 0) { + j = copy_node(i); + if (p == null) { + q = j; + } else { + vlink(p) = j; + } + p = j; + } + } + free(saved_varmem_sizes); +#endif + return q; +} + +@ @c +void print_node_mem_stats(void) +{ + int i, b; + halfword j; + char msg[256]; + char *s; + int free_chain_counts[MAX_CHAIN_SIZE] = { 0 }; + snprintf(msg, 255, " %d words of node memory still in use:", (int) (var_used + my_prealloc)); + tprint_nl(msg); + s = sprint_node_mem_usage(); + tprint_nl(" "); + tprint(s); + free(s); + tprint(" nodes"); + tprint_nl(" avail lists: "); + b = 0; + for (i = 1; i < MAX_CHAIN_SIZE; i++) { + for (j = free_chain[i]; j != null; j = vlink(j)) + free_chain_counts[i]++; + if (free_chain_counts[i] > 0) { + snprintf(msg, 255, "%s%d:%d", (b ? "," : ""), i, (int) free_chain_counts[i]); + tprint(msg); + b = 1; + } + } + /* newline, if needed */ + print_nlp(); +} + +/* this belongs in the web but i couldn't find the correct syntactic place */ + +halfword new_span_node(halfword n, int s, scaled w) +{ + halfword p = new_node(span_node, 0); + span_link(p) = n; + span_span(p) = s; + width(p) = w; + return p; +} + +@* Attribute stuff. + +@c +static halfword new_attribute_node(unsigned int i, int v) +{ + register halfword r = get_node(attribute_node_size); + type(r) = attribute_node; + attribute_id(r) = (halfword) i; + attribute_value(r) = v; + /* not used but nicer in print */ + subtype(r) = 0; + return r; +} + +@ @c +halfword copy_attribute_list(halfword n) +{ + halfword q = get_node(attribute_node_size); + register halfword p = q; + type(p) = attribute_list_node; + attr_list_ref(p) = 0; + n = vlink(n); + while (n != null) { + register halfword r = get_node(attribute_node_size); + /* the link will be fixed automatically in the next loop */ + (void) memcpy((void *) (varmem + r), (void *) (varmem + n), + (sizeof(memory_word) * attribute_node_size)); + vlink(p) = r; + p = r; + n = vlink(n); + } + return q; +} + +@ @c +void update_attribute_cache(void) +{ + halfword p; + register int i; + attr_list_cache = get_node(attribute_node_size); + type(attr_list_cache) = attribute_list_node; + attr_list_ref(attr_list_cache) = 0; + p = attr_list_cache; + for (i = 0; i <= max_used_attr; i++) { + register int v = attribute(i); + if (v > UNUSED_ATTRIBUTE) { + register halfword r = new_attribute_node((unsigned) i, v); + vlink(p) = r; + p = r; + } + } + if (vlink(attr_list_cache) == null) { + free_node(attr_list_cache, attribute_node_size); + attr_list_cache = null; + } + return; +} + +@ @c +void build_attribute_list(halfword b) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled|| attr_list_cache == null) { + update_attribute_cache(); + if (attr_list_cache == null) + return; + } + attr_list_ref(attr_list_cache)++; + node_attr(b) = attr_list_cache; + } +} + +@ @c +halfword current_attribute_list(void) +{ + if (max_used_attr >= 0) { + if (attr_list_cache == cache_disabled) { + update_attribute_cache(); + } + return attr_list_cache ; + } + return null ; +} + + +@ @c +void reassign_attribute(halfword n, halfword new) +{ + halfword old; + old = node_attr(n); + if (new == null) { + /* there is nothing to assign but we need to check for an old value */ + if (old != null) + delete_attribute_ref(old); /* also nulls attr field of n */ + } else if (old == null) { + /* nothing is assigned so we just do that now */ + assign_attribute_ref(n,new); + } else if (old != new) { + /* something is assigned so we need to clean up and assign then */ + delete_attribute_ref(old); + assign_attribute_ref(n,new); + } + /* else: same value so there is no need to assign and change the refcount */ + node_attr(n) = new ; +} + +@ @c +void delete_attribute_ref(halfword b) +{ + if (b != null) { + if (type(b) == attribute_list_node){ + attr_list_ref(b)--; + if (attr_list_ref(b) == 0) { + if (b == attr_list_cache) + attr_list_cache = cache_disabled; + free_node_chain(b, attribute_node_size); + } + /* maintain sanity */ + if (attr_list_ref(b) < 0) { + attr_list_ref(b) = 0; + } + } else { + normal_error("nodes","trying to delete an attribute reference of a non attribute node"); + } + } +} + +void reset_node_properties(halfword b) +{ + if (b != null) { + lua_properties_reset(b); + } +} + +@ |p| is an attr list head, or zero +@c +halfword do_set_attribute(halfword p, int i, int val) +{ + register halfword q; + register int j = 0; + if (p == null) { /* add a new head \& node */ + q = get_node(attribute_node_size); + type(q) = attribute_list_node; + attr_list_ref(q) = 1; + p = new_attribute_node((unsigned) i, val); + vlink(q) = p; + return q; + } + q = p; + if (vlink(p) != null) { + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) + return q; /* no need to do anything */ + if (t >= i) + break; + j++; + p = vlink(p); + } + + p = q; + while (j-- > 0) + p = vlink(p); + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { /* add a new node */ + halfword r = new_attribute_node((unsigned) i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } + return q; + } else { + normal_error("nodes","trying to set an attribute fails, case 1"); + return null ; + } +} + +@ @c +void set_attribute(halfword n, int i, int val) +{ + register halfword p; + register int j = 0; + /* not all nodes can have an attribute list */ + if (!nodetype_has_attributes(type(n))) + return; + /* if we have no list, we create one and quit */ + p = node_attr(n); + if (p == null) { /* add a new head \& node */ + p = get_node(attribute_node_size); + type(p) = attribute_list_node; + attr_list_ref(p) = 1; + node_attr(n) = p; + p = new_attribute_node((unsigned) i, val); + vlink(node_attr(n)) = p; + return; + } + /* we check if we have this attribute already and quit if the value stays the same */ + if (vlink(p) != null) { + while (vlink(p) != null) { + int t = attribute_id(vlink(p)); + if (t == i && attribute_value(vlink(p)) == val) + return; + if (t >= i) + break; + j++; + p = vlink(p); + } + /* j has now the position (if found) .. we assume a sorted list ! */ + p = node_attr(n); + + if (attr_list_ref(p) == 0 ) { + /* the list is invalid i.e. freed already */ + formatted_warning("nodes","node %d has an attribute list that is free already, case 1",(int) n); + /* the still dangling list gets ref count 1 */ + attr_list_ref(p) = 1; + } else if (attr_list_ref(p) == 1) { + /* this can really happen HH-LS */ + if (p == attr_list_cache) { + /* we can invalidate the cache setting */ + /* attr_list_cache = cache_disabled */ + /* or save the list, as done below */ + p = copy_attribute_list(p); + node_attr(n) = p; + /* the copied list gets ref count 1 */ + attr_list_ref(p) = 1; + } + } else { + /* the list is used multiple times so we make a copy */ + p = copy_attribute_list(p); + /* we decrement the ref count or the original */ + delete_attribute_ref(node_attr(n)); + node_attr(n) = p; + /* the copied list gets ref count 1 */ + attr_list_ref(p) = 1; + } + + + /* we go to position j in the list */ + while (j-- > 0) + p = vlink(p); + /* if we have a hit we just set the value otherwise we add a new node */ + if (attribute_id(vlink(p)) == i) { + attribute_value(vlink(p)) = val; + } else { /* add a new node */ + halfword r = new_attribute_node((unsigned) i, val); + vlink(r) = vlink(p); + vlink(p) = r; + } + } else { + normal_error("nodes","trying to set an attribute fails, case 2"); + } +} + +@ @c +int unset_attribute(halfword n, int i, int val) +{ + register halfword p; + register int t; + register int j = 0; + + if (!nodetype_has_attributes(type(n))) + return null; + p = node_attr(n); + if (p == null) + return UNUSED_ATTRIBUTE; + if (attr_list_ref(p) == 0) { + formatted_warning("nodes","node %d has an attribute list that is free already, case 2", (int) n); + return UNUSED_ATTRIBUTE; + } + if (vlink(p) != null) { + while (vlink(p) != null) { + t = attribute_id(vlink(p)); + if (t > i) + return UNUSED_ATTRIBUTE; + if (t == i) { + p = vlink(p); + break; + } + j++; + p = vlink(p); + } + if (attribute_id(p) != i) + return UNUSED_ATTRIBUTE; + /* if we are still here, the attribute exists */ + p = node_attr(n); + if (attr_list_ref(p) > 1 || p == attr_list_cache) { + halfword q = copy_attribute_list(p); + if (attr_list_ref(p) > 1) { + delete_attribute_ref(node_attr(n)); + } + attr_list_ref(q) = 1; + node_attr(n) = q; + } + p = vlink(node_attr(n)); + while (j-- > 0) + p = vlink(p); + t = attribute_value(p); + if (val == UNUSED_ATTRIBUTE || t == val) { + attribute_value(p) = UNUSED_ATTRIBUTE; + } + return t; + } else { + normal_error("nodes","trying to unset an attribute fails"); + return null; + } +} + +@ @c +int has_attribute(halfword n, int i, int val) +{ + register halfword p; + if (!nodetype_has_attributes(type(n))) + return UNUSED_ATTRIBUTE; + p = node_attr(n); + if (p == null || vlink(p) == null) + return UNUSED_ATTRIBUTE; + p = vlink(p); + while (p != null) { + if (attribute_id(p) == i) { + int ret = attribute_value(p); + if (val == UNUSED_ATTRIBUTE || val == ret) + return ret; + return UNUSED_ATTRIBUTE; + } else if (attribute_id(p) > i) { + return UNUSED_ATTRIBUTE; + } + p = vlink(p); + } + return UNUSED_ATTRIBUTE; +} + +@ @c +void print_short_node_contents(halfword p) +{ + switch (type(p)) { + case hlist_node: + case vlist_node: + case ins_node: + case whatsit_node: + case mark_node: + case adjust_node: + case unset_node: + print_char('['); + print_char(']'); + break; + case rule_node: + print_char('|'); + break; + case glue_node: + if (! glue_is_zero(p)) + print_char(' '); + break; + case math_node: + print_char('$'); + break; + case disc_node: + short_display(vlink(pre_break(p))); + short_display(vlink(post_break(p))); + break; + } +} + +@ @c +static void show_pdftex_whatsit_rule_spec(int p) +{ + tprint("("); + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); +} + +@ Each new type of node that appears in our data structure must be capable +of being displayed, copied, destroyed, and so on. The routines that we +need for write-oriented whatsits are somewhat like those for mark nodes; +other extensions might, of course, involve more subtlety here. + +@c +static void print_write_whatsit(const char *s, pointer p) +{ + tprint_esc(s); + if (write_stream(p) < 16) + print_int(write_stream(p)); + else if (write_stream(p) == 16) + print_char('*'); + else + print_char('-'); +} + +@ @c +static void show_node_wrapup_core(int p) +{ + switch (subtype(p)) { + case open_node: + print_write_whatsit("openout", p); + print_char('='); + print_file_name(open_name(p), open_area(p), open_ext(p)); + break; + case write_node: + print_write_whatsit("write", p); + print_mark(write_tokens(p)); + break; + case close_node: + print_write_whatsit("closeout", p); + break; + case special_node: + tprint_esc("special"); + print_mark(write_tokens(p)); + break; + case late_lua_node: + show_late_lua(p); + break; + case save_pos_node: + tprint_esc("savepos"); + break; + case user_defined_node: + tprint_esc("whatsit"); + print_int(user_node_id(p)); + print_char('='); + switch (user_node_type(p)) { + case 'a': + tprint("<>"); + break; + case 'n': + tprint("["); + show_node_list(user_node_value(p)); + tprint("]"); + break; + case 's': + print_char('"'); + print(user_node_value(p)); + print_char('"'); + break; + case 't': + print_mark(user_node_value(p)); + break; + default: /* only 'd' */ + print_int(user_node_value(p)); + break; + } + break; + } +} + +void show_node_wrapup_dvi(int p) +{ +} + +void show_node_wrapup_pdf(int p) +{ + switch (subtype(p)) { + case pdf_literal_node: + show_pdf_literal(p); + break; + case pdf_colorstack_node: + tprint_esc("pdfcolorstack "); + print_int(pdf_colorstack_stack(p)); + switch (pdf_colorstack_cmd(p)) { + case colorstack_set: + tprint(" set "); + break; + case colorstack_push: + tprint(" push "); + break; + case colorstack_pop: + tprint(" pop"); + break; + case colorstack_current: + tprint(" current"); + break; + default: + confusion("colorstack"); + break; + } + if (pdf_colorstack_cmd(p) <= colorstack_data) + print_mark(pdf_colorstack_data(p)); + break; + case pdf_setmatrix_node: + tprint_esc("pdfsetmatrix"); + print_mark(pdf_setmatrix_data(p)); + break; + case pdf_save_node: + tprint_esc("pdfsave"); + break; + case pdf_restore_node: + tprint_esc("pdfrestore"); + break; + case pdf_refobj_node: + tprint_esc("pdfrefobj"); + if (obj_obj_is_stream(static_pdf, pdf_obj_objnum(p))) { + if (obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + tprint(" attr"); + lua_rawgeti(Luas, LUA_REGISTRYINDEX, + obj_obj_stream_attr(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } + tprint(" stream"); + } + if (obj_obj_is_file(static_pdf, pdf_obj_objnum(p))) + tprint(" file"); + if (obj_obj_data(static_pdf, pdf_obj_objnum(p)) != LUA_NOREF) { + lua_rawgeti(Luas, LUA_REGISTRYINDEX, + obj_obj_data(static_pdf, pdf_obj_objnum(p))); + print_char(' '); + tprint((const char *) lua_tostring(Luas, -1)); + lua_pop(Luas, 1); + } + break; + case pdf_annot_node: + tprint_esc("pdfannot"); + show_pdftex_whatsit_rule_spec(p); + print_mark(pdf_annot_data(p)); + break; + case pdf_start_link_node: + tprint_esc("pdfstartlink"); + show_pdftex_whatsit_rule_spec(p); + if (pdf_link_attr(p) != null) { + tprint(" attr"); + print_mark(pdf_link_attr(p)); + } + tprint(" action"); + if (pdf_action_type(pdf_link_action(p)) == pdf_action_user) { + tprint(" user"); + print_mark(pdf_action_tokens(pdf_link_action(p))); + return; + } + if (pdf_action_file(pdf_link_action(p)) != null) { + tprint(" file"); + print_mark(pdf_action_file(pdf_link_action(p))); + } + switch (pdf_action_type(pdf_link_action(p))) { + case pdf_action_goto: + if (pdf_action_named_id(pdf_link_action(p)) > 0) { + tprint(" goto name"); + print_mark(pdf_action_id(pdf_link_action(p))); + } else { + tprint(" goto num"); + print_int(pdf_action_id(pdf_link_action(p))); + } + break; + case pdf_action_page: + tprint(" page"); + print_int(pdf_action_id(pdf_link_action(p))); + print_mark(pdf_action_tokens(pdf_link_action(p))); + break; + case pdf_action_thread: + if (pdf_action_named_id(pdf_link_action(p)) > 0) { + tprint(" thread name"); + print_mark(pdf_action_id(pdf_link_action(p))); + } else { + tprint(" thread num"); + print_int(pdf_action_id(pdf_link_action(p))); + } + break; + default: + normal_error("pdf backend", "unknown action type for link"); + break; + } + break; + case pdf_end_link_node: + tprint_esc("pdfendlink"); + break; + case pdf_dest_node: + tprint_esc("pdfdest"); + if (pdf_dest_named_id(p) > 0) { + tprint(" name"); + print_mark(pdf_dest_id(p)); + } else { + tprint(" num"); + print_int(pdf_dest_id(p)); + } + print_char(' '); + switch (pdf_dest_type(p)) { + case pdf_dest_xyz: + tprint("xyz"); + if (pdf_dest_xyz_zoom(p) != null) { + tprint(" zoom"); + print_int(pdf_dest_xyz_zoom(p)); + } + break; + case pdf_dest_fitbh: + tprint("fitbh"); + break; + case pdf_dest_fitbv: + tprint("fitbv"); + break; + case pdf_dest_fitb: + tprint("fitb"); + break; + case pdf_dest_fith: + tprint("fith"); + break; + case pdf_dest_fitv: + tprint("fitv"); + break; + case pdf_dest_fitr: + tprint("fitr"); + show_pdftex_whatsit_rule_spec(p); + break; + case pdf_dest_fit: + tprint("fit"); + break; + default: + tprint("unknown!"); + break; + } + break; + case pdf_thread_node: + case pdf_start_thread_node: + if (subtype(p) == pdf_thread_node) + tprint_esc("pdfthread"); + else + tprint_esc("pdfstartthread"); + tprint("("); + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); + if (pdf_thread_attr(p) != null) { + tprint(" attr"); + print_mark(pdf_thread_attr(p)); + } + if (pdf_thread_named_id(p) > 0) { + tprint(" name"); + print_mark(pdf_thread_id(p)); + } else { + tprint(" num"); + print_int(pdf_thread_id(p)); + } + break; + case pdf_end_thread_node: + tprint_esc("pdfendthread"); + break; + default: + break; + } +} + +@ 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 + 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. + + +@ |str_room| need not be checked; see |show_box| + +@ Recursive calls on |show_node_list| therefore use the following pattern: +@c +#define node_list_display(A) do { \ + append_char('.'); \ + show_node_list(A); \ + flush_char(); \ +} while (0) + +#define node_list_display_x(A,B) do { \ + if ((B) != null) { \ + append_char('.'); \ + append_char(A); \ + append_char(' '); \ + show_node_list(B); \ + flush_char(); \ + flush_char(); \ + flush_char(); \ + } \ +} while (0) + +/* prints a node list symbolically */ + +void show_node_list(int p) +{ + int n = 0; /* the number of items already printed at this level */ + halfword w; + real g; /* a glue ratio, as a floating point number */ + if ((int) cur_length > depth_threshold) { + if (p > null) + tprint(" []"); /* indicate that there's been some truncation */ + return; + } + while (p != null) { + print_ln(); + print_current_string(); /* display the nesting history */ + if (tracing_online_par < -2) + print_int(p); + incr(n); + if (n > breadth_max) { /* time to stop */ + tprint("etc."); + return; + } + /* Display node |p| */ + if (is_char_node(p)) { + print_font_and_char(p); + if (is_ligature(p)) { + /* Display ligature |p|; */ + tprint(" (ligature "); + if (is_leftboundary(p)) + print_char('|'); + font_in_short_display = font(p); + short_display(lig_ptr(p)); + if (is_rightboundary(p)) + print_char('|'); + print_char(')'); + } + } else { + switch (type(p)) { + case hlist_node: + case vlist_node: + case unset_node: + /* Display box |p|; */ + if (type(p) == hlist_node) + tprint_esc("h"); + else if (type(p) == vlist_node) + tprint_esc("v"); + else + tprint_esc("unset"); + tprint("box("); + print_scaled(height(p)); + print_char('+'); + print_scaled(depth(p)); + tprint(")x"); + print_scaled(width(p)); + if (type(p) == unset_node) { + /* Display special fields of the unset node |p|; */ + if (span_count(p) != min_quarterword) { + tprint(" ("); + print_int(span_count(p) + 1); + tprint(" columns)"); + } + if (glue_stretch(p) != 0) { + tprint(", stretch "); + print_glue(glue_stretch(p), glue_order(p), NULL); + } + if (glue_shrink(p) != 0) { + tprint(", shrink "); + print_glue(glue_shrink(p), glue_sign(p), NULL); + } + } else { + /* Display the value of |glue_set(p)| */ + /* The code will have to change in this place if |glue_ratio| is + a structured type instead of an ordinary |real|. Note that this routine + should avoid arithmetic errors even if the |glue_set| field holds an + arbitrary random value. The following code assumes that a properly + formed nonzero |real| number has absolute value $2^{20}$ or more when + it is regarded as an integer; this precaution was adequate to prevent + floating point underflow on the author's computer. + */ + + g = (real) (glue_set(p)); + if ((g != 0.0) && (glue_sign(p) != normal)) { + tprint(", glue set "); + if (glue_sign(p) == shrinking) + tprint("- "); + if (g > 20000.0 || g < -20000.0) { + if (g > 0.0) + print_char('>'); + else + tprint("< -"); + print_glue(20000 * unity, glue_order(p), NULL); + } else { + print_glue(round(unity * g), glue_order(p), NULL); + } + } + + if (shift_amount(p) != 0) { + tprint(", shifted "); + print_scaled(shift_amount(p)); + } + tprint(", direction "); + print_dir(box_dir(p)); + } + node_list_display(list_ptr(p)); /* recursive call */ + break; + case rule_node: + /* Display rule |p|; */ + if (subtype(p) == normal_rule) { + tprint_esc("rule("); + } else if (subtype(p) == empty_rule) { + tprint_esc("norule("); + } else if (subtype(p) == user_rule) { + tprint_esc("userrule("); + } else if (subtype(p) == box_rule) { + tprint_esc("box("); + } else if (subtype(p) == image_rule) { + tprint_esc("image("); + } + print_rule_dimen(height(p)); + print_char('+'); + print_rule_dimen(depth(p)); + tprint(")x"); + print_rule_dimen(width(p)); + break; + case ins_node: + /* Display insertion |p|; */ + tprint_esc("insert"); + print_int(subtype(p)); + tprint(", natural size "); + print_scaled(height(p)); + tprint("; split("); + print_spec(split_top_ptr(p), NULL); + print_char(','); + print_scaled(depth(p)); + tprint("); float cost "); + print_int(float_cost(p)); + node_list_display(ins_ptr(p)); /* recursive call */ + break; + case dir_node: + if (dir_dir(p) < 0) { + tprint_esc("enddir"); + print_char(' '); + print_dir(dir_dir(p) + dir_swap); + } else { + tprint_esc("begindir"); + print_char(' '); + print_dir(dir_dir(p)); + } + break; + case local_par_node: + tprint_esc("localpar"); + append_char('.'); + print_ln(); + print_current_string(); + tprint_esc("localinterlinepenalty"); + print_char('='); + print_int(local_pen_inter(p)); + print_ln(); + print_current_string(); + tprint_esc("localbrokenpenalty"); + print_char('='); + print_int(local_pen_broken(p)); + print_ln(); + print_current_string(); + tprint_esc("localleftbox"); + if (local_box_left(p) == null) { + tprint("=null"); + } else { + append_char('.'); + show_node_list(local_box_left(p)); + decr(cur_length); + } + print_ln(); + print_current_string(); + tprint_esc("localrightbox"); + if (local_box_right(p) == null) { + tprint("=null"); + } else { + append_char('.'); + show_node_list(local_box_right(p)); + decr(cur_length); + } + decr(cur_length); + break; + case boundary_node: + if (subtype(p)==0) { + tprint_esc("noboundary"); + } else { + switch (subtype(p)) { + case 1: + tprint_esc("boundary"); + break; + case 2: + tprint_esc("protrusionboundary"); + break; + case 3: + tprint_esc("wordboundary"); + break; + default: + tprint_esc("boundary"); + print_char(':'); + print_int(subtype(p)); + break; + } + print_char('='); + print_int(boundary_value(p)); + } + break; + case whatsit_node: + w = subtype(p) ; + if (w >= backend_first_pdf_whatsit) { + show_node_wrapup_pdf(p); + } else if (w >= backend_first_dvi_whatsit) { + show_node_wrapup_dvi(p); + } else { + show_node_wrapup_core(p); + } + break; + case glue_node: + /* Display glue |p|; */ + if (subtype(p) >= a_leaders) { + /* Display leaders |p|; */ + tprint_esc(""); + switch (subtype(p)) { + case a_leaders: + break; + case c_leaders: + print_char('c'); + break; + case x_leaders: + print_char('x'); + break; + case g_leaders: + print_char('g'); + break; + default: + normal_warning("nodes","weird glue leader subtype ignored"); + } + tprint("leaders "); + print_spec(p, NULL); + node_list_display(leader_ptr(p)); /* recursive call */ + } else { + tprint_esc("glue"); + if (subtype(p) != normal) { + print_char('('); + if ((subtype(p) - 1) < thin_mu_skip_code) { + print_cmd_chr(assign_glue_cmd, glue_base + (subtype(p) - 1)); + } else if (subtype(p) < cond_math_glue) { + print_cmd_chr(assign_mu_glue_cmd, glue_base + (subtype(p) - 1)); + } else if (subtype(p) == cond_math_glue) { + tprint_esc("nonscript"); + } else { + tprint_esc("mskip"); + } + print_char(')'); + } + if (subtype(p) != cond_math_glue) { + print_char(' '); + if (subtype(p) < cond_math_glue) + print_spec(p, NULL); + else + print_spec(p, "mu"); + } + } + break; + case margin_kern_node: + tprint_esc("kern"); + print_scaled(width(p)); + if (subtype(p) == left_side) + tprint(" (left margin)"); + else + tprint(" (right margin)"); + break; + case kern_node: + /* Display kern |p|; */ + /* An ``explicit'' kern value is indicated implicitly by an explicit space. */ + if (subtype(p) != mu_glue) { + tprint_esc("kern"); + if (subtype(p) != normal) + print_char(' '); + print_scaled(width(p)); + if (subtype(p) == accent_kern) + tprint(" (for accent)"); + } else { + tprint_esc("mkern"); + print_scaled(width(p)); + tprint("mu"); + } + break; + case math_node: + /* Display math node |p|; */ + tprint_esc("math"); + if (subtype(p) == before) + tprint("on"); + else + tprint("off"); + if (!glue_is_zero(p)) { + tprint(", glued "); + print_spec(p, NULL); + } else if (surround(p) != 0) { + tprint(", surrounded "); + print_scaled(surround(p)); + } + break; + case penalty_node: + /* Display penalty |p|; */ + tprint_esc("penalty "); + print_int(penalty(p)); + break; + case disc_node: + /* Display discretionary |p|; */ + /* The |post_break| list of a discretionary node is indicated by a prefixed + `\.{\char'174}' instead of the `\..' before the |pre_break| list. */ + /* We're not compatible anyway so ... + tprint_esc("discretionary"); + print_int(disc_penalty(p)); + print_char('|'); + if (vlink(no_break(p)) != null) { + tprint(" replacing "); + node_list_display(vlink(no_break(p))); + } + node_list_display(vlink(pre_break(p))); + append_char('|'); + show_node_list(vlink(post_break(p))); + flush_char(); + */ + tprint_esc("discretionary"); + tprint(" (penalty "); + print_int(disc_penalty(p)); + print_char(')'); + node_list_display_x('<',vlink(pre_break(p))); + node_list_display_x('>',vlink(post_break(p))); + node_list_display_x('=',vlink(no_break(p))); + break; + case mark_node: + /* Display mark |p|; */ + tprint_esc("mark"); + if (mark_class(p) != 0) { + print_char('s'); + print_int(mark_class(p)); + } + print_mark(mark_ptr(p)); + break; + case adjust_node: + /* Display adjustment |p|; */ + tprint_esc("vadjust"); + if (subtype(p) != 0) + tprint(" pre "); + node_list_display(adjust_ptr(p)); /* recursive call */ + break; + case glue_spec_node: + tprint(""); + break; + default: + show_math_node(p); + break; + } + } + p = vlink(p); + } +} + +@ This routine finds the 'base' width of a horizontal box, using the same logic + that \TeX82 used for \.{\\predisplaywidth} */ + +@c +static pointer get_actual_box_width(pointer r,pointer p, scaled initial_width) +{ + scaled d; /* increment to |v| */ + scaled w = -max_dimen; /* calculated |size| */ + scaled v = initial_width; /* |w| plus possible glue amount */ + while (p != null) { + if (is_char_node(p)) { + d = glyph_width(p); + goto FOUND; + } + switch (type(p)) { + case hlist_node: + case vlist_node: + case rule_node: + d = width(p); + goto FOUND; + break; + case margin_kern_node: + d = width(p); + break; + case kern_node: + d = width(p); + break; + case disc_node: + /* at the end of the line we should actually take the pre */ + if (no_break(p) != null) { + d = get_actual_box_width(r,vlink_no_break(p),0); + if (d <= -max_dimen || d >= max_dimen) { + d = 0; + } + } else { + d = 0; + } + goto FOUND; + break; + case math_node: + /* begin mathskip code */ + if (glue_is_zero(p)) { + d = surround(p); + break; + } else { + /* fall through */ + } + /* end mathskip code */ + case glue_node: + /* We need to be careful that |w|, |v|, and |d| do not depend on any |glue_set| + values, since such values are subject to system-dependent rounding. + System-dependent numbers are not allowed to infiltrate parameters like + |pre_display_size|, since \TeX82 is supposed to make the same decisions on all + machines. + */ + d = width(p); + if (glue_sign(r) == stretching) { + if ((glue_order(r) == stretch_order(p)) && (stretch(p) != 0)) + v = max_dimen; + } else if (glue_sign(r) == shrinking) { + if ((glue_order(r) == shrink_order(p)) && (shrink(p) != 0)) + v = max_dimen; + } + if (subtype(p) >= a_leaders) + goto FOUND; + break; + default: + d = 0; + break; + } + if (v < max_dimen) + v = v + d; + goto NOT_FOUND; + FOUND: + if (v < max_dimen) { + v = v + d; + w = v; + } else { + w = max_dimen; + break; + } + NOT_FOUND: + p = vlink(p); + } + return w; +} + +pointer actual_box_width(pointer r, scaled base_width) +{ + /* often this is the same as: + return + shift_amount(r) + base_width + + natural_sizes(list_ptr(r),null,(glue_ratio) glue_set(r),glue_sign(r),glue_order(r),box_dir(r)); + */ + return get_actual_box_width(r,list_ptr(r),shift_amount(r) + base_width); +} + +@ @c +halfword tail_of_list(halfword p) +{ + halfword q = p; + while (vlink(q) != null) + q = vlink(q); + return q; +} + + + +@ @c +int var_used; + +@ Attribute lists need two extra globals to increase processing efficiency. +|max_used_attr| limits the test loop that checks for set attributes, and +|attr_list_cache| contains a pointer to an already created attribute list. It is +set to the special value |cache_disabled| when the current value can no longer be +trusted: after an assignment to an attribute register, and after a group has +ended. + +@c +int max_used_attr; /* maximum assigned attribute id */ +halfword attr_list_cache; + +@ 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|'. + +@ Character nodes appear only in horizontal lists, never in vertical lists. + +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|, +|sfi|, |fil|, |fill|, or |filll|). The |subtype| field is not used. + +@ 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|. + +@c +halfword new_null_box(void) +{ /* creates a new box node */ + halfword p = new_node(hlist_node, min_quarterword); + box_dir(p) = text_direction_par; + return p; +} + +@ A |vlist_node| is like an |hlist_node| in all respects except that it +contains a vertical list. + +@ 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. + +@ 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. + +@c +halfword new_rule(int s) +{ + halfword p = new_node(rule_node,s); + 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. + +@ A |mark_node| has a |mark_ptr| field that points to the reference count +of a token list that contains the user's \.{\\mark} text. +In addition there is a |mark_class| field that contains the mark class. + +@ 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. + +@ A |glyph_node|, which occurs only in horizontal lists, specifies a +glyph in a particular font, along with its attribute list. Older +versions of \TeX\ could use token memory for characters, because the +font,char combination would fit in a single word (both values were +required to be strictly less than $2^{16}$). In LuaTeX, room is +needed for characters that are larger than that, as well as a pointer +to a potential attribute list, and the two displacement values. + +In turn, that made the node so large that it made sense to merge +ligature glyphs as well, as that requires only one extra pointer. A +few extra classes of glyph nodes will be introduced later. The +unification of all those types makes it easier to manipulate lists of +glyphs. The subtype differentiates various glyph kinds. + +First, here is a function that returns a pointer to a glyph node for a given +glyph in a given font. If that glyph doesn't exist, |null| is returned +instead. Nodes of this subtype are directly created only for accents +and their base (through |make_accent|), and math nucleus items (in the +conversion from |mlist| to |hlist|). + +@c +halfword new_glyph(int f, int c) +{ + halfword p = null; /* the new node */ + if ((f == 0) || (char_exists(f, c))) { + p = new_glyph_node(); + set_to_glyph(p); + font(p) = f; + character(p) = c; + } + return p; +} + +@ A subset of the glyphs nodes represent ligatures: characters +fabricated from the interaction of two or more actual characters. The +characters that generated the ligature have not been forgotten, since +they are needed for diagnostic messages; 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 of these |glyph_node|s is 1, plus 2 and/or 1 if +the original source of the ligature included implicit left and/or +right boundaries. These nodes are created by the C function |new_ligkern|. + +A third general type of glyphs could be called a character, as it +only appears in lists that are not yet processed by the ligaturing and +kerning steps of the program. + +|main_control| inserts these, and they are later converted to +|subtype_normal| by |new_ligkern|. + +@c +quarterword norm_min(int h) +{ + if (h <= 0) + return 1; + else if (h >= 255) + return 255; + else + return (quarterword) h; +} + +halfword new_char(int f, int c) +{ + halfword p; /* the new node */ + p = new_glyph_node(); + set_to_character(p); + font(p) = f; + character(p) = c; + lang_data(p) = make_lang_data(uc_hyph_par, cur_lang_par, left_hyphen_min_par, right_hyphen_min_par); + return p; +} + +@ Left and right ghost glyph nodes are the result of \.{\\leftghost} +and \.{\\rightghost}, respectively. They are going to be removed by +|new_ligkern|, at the end of which they are no longer needed. + +@ Here are a few handy helpers used by the list output routines. + +@c +scaled glyph_width(halfword p) +{ + scaled w = char_width(font(p), character(p)); + return w; +} + +scaled glyph_height(halfword p) +{ + scaled w = char_height(font(p), character(p)) + y_displace(p); + if (w < 0) + w = 0; + return w; +} + +scaled glyph_depth(halfword p) +{ + scaled w = char_depth(font(p), character(p)); + if (y_displace(p) > 0) + w = w - y_displace(p); + if (w < 0) + w = 0; + return w; +} + +@ 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 +|no_break(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 |no_break=null|. + +{TODO: Knuth said: All three of the discretionary texts must be lists +that consist entirely of character, kern, box and rule nodes.} + +If |subtype(p)=automatic_disc|, 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. + +@c +halfword new_disc(void) +{ /* creates an empty |disc_node| */ + halfword p = new_node(disc_node, 0); + disc_penalty(p) = hyphen_penalty_par; + 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. + +@ 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 |surround| field, which represents +the amount of surrounding space inserted by \.{\\mathsurround}. + +@c +halfword new_math(scaled w, int s) +{ + halfword p = new_node(math_node, s); + surround(p) = w; + return p; +} + +@ \TeX\ makes use of the fact that |hlist_node|, |vlist_node|, +|rule_node|, |ins_node|, |mark_node|, |adjust_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. + +@ 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. + +@ 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|, |sfi|, |fil|, |fill|, or |filll|) +corresponding to the stretch and shrink values. + +@ 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. + +@c +halfword new_spec(halfword q) /* safeguard for copying a glue node */ +{ + if (q == null) { + return copy_node(zero_glue); + } else if (type(q) == glue_spec_node) { + return copy_node(q); + } else if (type(q) == glue_node) { + halfword p = copy_node(zero_glue); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; + } else { + /* alternatively we can issue a warning */ + return copy_node(zero_glue); + } +} + +@ 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}. + +@c +halfword new_param_glue(int n) +{ + halfword p = new_node(glue_node, n + 1); + halfword q = glue_par(n); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + return p; +} + +@ Glue nodes that are more or less anonymous are created by |new_glue|, +whose argument points to a glue specification. + +@c +halfword new_glue(halfword q) +{ + halfword p = new_node(glue_node, normal); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(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. +*/ + +@c +halfword new_skip_param(int n) +{ + halfword p = new_node(glue_node, n + 1); + halfword q = glue_par(n); + width(p) = width(q); + stretch(p) = stretch(q); + shrink(p) = shrink(q); + stretch_order(p) = stretch_order(q); + shrink_order(p) = shrink_order(q); + 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). + +@ The |new_kern| function creates a kern node having a given width. + +@c +halfword new_kern(scaled w) +{ + halfword p = new_node(kern_node, 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. + +@ Anyone who has been reading the last few sections of the program will +be able to guess what comes next. + +@c +halfword new_penalty(int m, int s) +{ + halfword p = new_node(penalty_node, 0); /* the |subtype| is not used */ + penalty(p) = m; + subtype(p) = s; + 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. + +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@> + +@ This function creates a |local_paragraph| node + +@c + +halfword make_local_par_node(int mode) +{ + int callback_id; + halfword q; + halfword p = new_node(local_par_node,0); + local_pen_inter(p) = local_inter_line_penalty_par; + local_pen_broken(p) = local_broken_penalty_par; + if (local_left_box_par != null) { + q = copy_node_list(local_left_box_par); + local_box_left(p) = q; + local_box_left_width(p) = width(local_left_box_par); + } + if (local_right_box_par != null) { + q = copy_node_list(local_right_box_par); + local_box_right(p) = q; + local_box_right_width(p) = width(local_right_box_par); + } + local_par_dir(p) = par_direction_par; + /* callback with node passed */ + callback_id = callback_defined(insert_local_par_callback); + if (callback_id > 0) { + int sfix = lua_gettop(Luas); + if (!get_callback(Luas, callback_id)) { + lua_settop(Luas, sfix); + return p; + } + nodelist_to_lua(Luas, p); + lua_push_local_par_mode(Luas,mode) + if (lua_pcall(Luas, 2, 0, 0) != 0) { /* 2 arg, 0 result */ + char errmsg[256]; /* temp hack ... we will have a formatted error */ + snprintf(errmsg, 255, "error: %s\n", lua_tostring(Luas, -1)); + errmsg[255]='\0'; + lua_settop(Luas, sfix); + normal_error("insert_local_par",errmsg); /* to be done */ + return p; + } + lua_settop(Luas, sfix); + } + /* done */ + return p; +} diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.h b/Build/source/texk/web2c/luatexdir/tex/textoken.h index 9a9ef501cf6..4f46a48c819 100644 --- a/Build/source/texk/web2c/luatexdir/tex/textoken.h +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.h @@ -174,6 +174,7 @@ extern void read_toks(int n, halfword r, halfword j); extern str_number tokens_to_string(halfword p); /* return a string from tokens list */ +extern char *tokenlist_to_xstring(int p, int inhibit_par, int *siz); extern char *tokenlist_to_cstring(int p, int inhibit_par, int *siz); extern lstring *tokenlist_to_lstring(int pp, int inhibit_par); extern void free_lstring(lstring * ls); diff --git a/Build/source/texk/web2c/luatexdir/tex/textoken.w b/Build/source/texk/web2c/luatexdir/tex/textoken.w index 196ecf8c399..f0b6a0c6e75 100644 --- a/Build/source/texk/web2c/luatexdir/tex/textoken.w +++ b/Build/source/texk/web2c/luatexdir/tex/textoken.w @@ -3382,6 +3382,147 @@ char *tokenlist_to_cstring(int pp, int inhibit_par, int *siz) return ret; } +char *tokenlist_to_xstring(int pp, int inhibit_par, int *siz) +{ + register int p, c, m; + int q; + int infop; + char *s, *sh; + int e = 0; + char *ret; + int match_chr = '#'; + int n = '0'; + unsigned alloci = 1024; + int i = 0; + int skipping = 1; + p = pp; + if (p == null) { + if (siz != NULL) + *siz = 0; + return NULL; + } + ret = xmalloc(alloci); + p = token_link(p); /* skip refcount */ + if (p != null) { + e = escape_char_par; + } + while (p != null) { + if (p < (int) fix_mem_min || p > (int) fix_mem_end) { + /* nothing */ + break; + } + infop = token_info(p); + if (infop >= cs_token_flag) { + if (!(inhibit_par && infop == par_token)) { + q = infop - cs_token_flag; + if (q < hash_base) { + /* nothing */ + } else if ((q >= undefined_control_sequence) && ((q <= eqtb_size) || (q > eqtb_size + hash_extra))) { + /* nothing */ + } else if ((cs_text(q) < 0) || (cs_text(q) >= str_ptr)) { + /* nothing */ + } else { +if (!skipping) { + str_number txt = cs_text(q); + sh = makecstring(txt); + s = sh; + if (is_active_cs(txt)) { + s = s + 3; + while (*s) { + Print_char(*s); + s++; + } + } else { + if (e>=0 && e<0x110000) Print_uchar(e); + while (*s) { + Print_char(*s); + s++; + } + if ((!single_letter(txt)) || is_cat_letter(txt)) { + Print_char(' '); + } + } + free(sh); +} + } + } + } else { + if (infop < 0) { + /* nothing */ + } else { + m = token_cmd(infop); + c = token_chr(infop); + switch (m) { + case left_brace_cmd: + case right_brace_cmd: + case math_shift_cmd: + case tab_mark_cmd: + case sup_mark_cmd: + case sub_mark_cmd: + case spacer_cmd: + case letter_cmd: + case other_char_cmd: +if (!skipping) { + Print_uchar(c); +} + break; + case mac_param_cmd: +if (!skipping) { + if (!in_lua_escape && (is_in_csname==0)) + Print_uchar(c); + Print_uchar(c); +} + break; + case out_param_cmd: +if (!skipping) { + Print_uchar(match_chr); +} + if (c <= 9) { +if (!skipping) { + Print_char(c + '0'); +} + } else { + /* nothing */ + goto EXIT; + } + break; + case match_cmd: + match_chr = c; +if (!skipping) { + Print_uchar(c); +} + n++; +if (!skipping) { + Print_char(n); +} + if (n > '9') + goto EXIT; + break; + case end_match_cmd: + if (c == 0) { +if (!skipping) { + Print_char('-'); + Print_char('>'); +} + i = 0; +skipping = 0 ; + } + break; + default: + /* nothing */ + break; + } + } + } + p = token_link(p); + } + EXIT: + ret[i] = '\0'; + if (siz != NULL) + *siz = i; + return ret; +} + @ @c lstring *tokenlist_to_lstring(int pp, int inhibit_par) { diff --git a/Build/source/texk/web2c/luatexdir/utils/utils.w b/Build/source/texk/web2c/luatexdir/utils/utils.w index 2c23b1d3268..e947fb77df5 100644 --- a/Build/source/texk/web2c/luatexdir/utils/utils.w +++ b/Build/source/texk/web2c/luatexdir/utils/utils.w @@ -322,6 +322,11 @@ scaled divide_scaled(scaled s, scaled m, int dd) return sign * q; } +#ifdef _WIN32 +#undef floor +#define floor win32_floor +#endif + @ Same function, but using doubles instead of integers (faster) @c scaled divide_scaled_n(double sd, double md, double n) @@ -364,3 +369,115 @@ double rint(double x) } #endif + +@ replace tmpfile() on Windows +@c +#if defined(_WIN32) +/* _cairo_win_tmpfile (void) - replace tmpfile() on Windows + * extracted from cairo-misc.c in cairo - a vector graphics library + * with display and print output + * the functiion name is changed from + * _cairo_win32_tmpfile (void) to + * _cairo_win_tmpfile (void) + * + * + * Copyright 2002 University of Southern California + * Copyright 2005 Red Hat, Inc. + * Copyright 2007 Adrian Johnson + * + * This library is free software; you can redistribute it and/or + * modify it either under the terms of the GNU Lesser General Public + * License version 2.1 as published by the Free Software Foundation + * (the "LGPL") or, at your option, under the terms of the Mozilla + * Public License Version 1.1 (the "MPL"). If you do not alter this + * notice, a recipient may use your version of this file under either + * the MPL or the LGPL. + * + * You should have received a copy of the LGPL along with this library + * in the file COPYING-LGPL-2.1; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA + * You should have received a copy of the MPL along with this library + * in the file COPYING-MPL-1.1 + * + * The contents of this file are subject to the Mozilla Public License + * Version 1.1 (the "License"); you may not use this file except in + * compliance with the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY + * OF ANY KIND, either express or implied. See the LGPL or the MPL for + * the specific language governing rights and limitations. + * + * The Original Code is the cairo graphics library. + * + * The Initial Developer of the Original Code is University of Southern + * California. + * + * Contributor(s): + * Carl D. Worth + * Adrian Johnson + */ + +#include +#define WIN32_LEAN_AND_MEAN +/* We require Windows 2000 features such as ETO_PDY */ +#if !defined(WINVER) || (WINVER < 0x0500) +# define WINVER 0x0500 +#endif +#if !defined(_WIN32_WINNT) || (_WIN32_WINNT < 0x0500) +# define _WIN32_WINNT 0x0500 +#endif + +#include +#include + +/* tmpfile() replacement for Windows. + * + * On Windows tmpfile() creates the file in the root directory. This + * may fail due to unsufficient privileges. However, this isn't a + * problem on Windows CE so we don't use it there. + */ +FILE * +_cairo_win_tmpfile (void) +{ + DWORD path_len; + WCHAR path_name[MAX_PATH + 1]; + WCHAR file_name[MAX_PATH + 1]; + HANDLE handle; + int fd; + FILE *fp; + + path_len = GetTempPathW (MAX_PATH, path_name); + if (path_len <= 0 || path_len >= MAX_PATH) + return NULL; + + if (GetTempFileNameW (path_name, L"ps_", 0, file_name) == 0) + return NULL; + + handle = CreateFileW (file_name, + GENERIC_READ | GENERIC_WRITE, + 0, + NULL, + CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE, + NULL); + if (handle == INVALID_HANDLE_VALUE) { + DeleteFileW (file_name); + return NULL; + } + + fd = _open_osfhandle((intptr_t) handle, 0); + if (fd < 0) { + CloseHandle (handle); + return NULL; + } + + fp = _fdopen(fd, "w+b"); + if (fp == NULL) { + _close(fd); + return NULL; + } + + return fp; +} +#endif diff --git a/Build/source/texk/web2c/mplibdir/am/libmplib.am b/Build/source/texk/web2c/mplibdir/am/libmplib.am index ca2d8e4c969..3d57231194f 100644 --- a/Build/source/texk/web2c/mplibdir/am/libmplib.am +++ b/Build/source/texk/web2c/mplibdir/am/libmplib.am @@ -5,14 +5,19 @@ ## libmplib.a, used by MetaPost and luaTeX ## -EXTRA_LIBRARIES += libmplib.a +EXTRA_LIBRARIES += libmplibcore.a libmplibbackends.a -libmplib_a_CPPFLAGS = $(MPFR_INCLUDES) $(GMP_INCLUDES) $(CAIRO_INCLUDES) $(PIXMAN_INCLUDES) \ +libmplibcore_a_CPPFLAGS = $(MPFR_INCLUDES) $(GMP_INCLUDES) $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/mplibdir +libmplibbackends_a_CPPFLAGS = $(MPFR_INCLUDES) $(GMP_INCLUDES) $(CAIRO_INCLUDES) $(PIXMAN_INCLUDES) \ $(LIBPNG_INCLUDES) $(ZLIB_INCLUDES) $(AM_CPPFLAGS) -I$(srcdir)/mplibdir -## libmplib C sources -nodist_libmplib_a_SOURCES = tfmin.c $(mp_c_h) $(mpmath_c_h) $(mpmathbinary_c_h) $(mpmathdecimal_c_h) \ - $(mpmathdouble_c_h) $(mpstrings_c_h) $(pngout_c_h) $(psout_c_h) $(svgout_c_h) +## libmplib C sources core + backends +nodist_libmplibcore_a_SOURCES = tfmin.c $(mp_c_h) $(mpmath_c_h) $(mpmathbinary_c_h) $(mpmathdecimal_c_h) \ + $(mpmathdouble_c_h) $(mpstrings_c_h) $(psout_c_h) +nodist_libmplibbackends_a_SOURCES = $(pngout_c_h) $(svgout_c_h) + + + mp_c_h = mp.c mplib.h mpmp.h mpmath_c_h = mpmath.h mpmath.c mpmathbinary_c_h = mpmathbinary.h mpmathbinary.c @@ -28,7 +33,7 @@ tfmin.c: ctangle$(EXEEXT) mplibdir/tfmin.w $(mp_ctangle) tfmin.w # Creating several files: need stamp file and two rules with identical recipes -$(mp_c_h): mp-tangle +$(mp_c_h): mp-tangle @$(mp_ctangle_sh) mp mp-tangle: ctangle$(EXEEXT) mplibdir/mp.w tangle-sh @$(mp_ctangle_sh) mp @@ -78,11 +83,16 @@ libmplib_web = mplibdir/mp.w mplibdir/psout.w mplibdir/svgout.w mplibdir/pngout. libmplib_web += mplibdir/mpmath.w mplibdir/mpmathbinary.w mplibdir/mpmathdecimal.w libmplib_web += mplibdir/mpmathdouble.w mplibdir/mpstrings.w mplibdir/tfmin.w -$(libmplib_a_OBJECTS): $(nodist_libmplib_a_SOURCES) $(KPATHSEA_DEPEND) $(CAIRO_DEPEND) $(MPFR_DEPEND) +## core need headers backends +$(nodist_libmplibcore_a_SOURCES): $(svgout_c_h) $(pngout_c_h) + +$(libmplibcore_a_OBJECTS): $(nodist_libmplibcore_a_SOURCES) $(KPATHSEA_DEPEND) $(MPFR_DEPEND) +$(libmplibbackends_a_OBJECTS): $(nodist_libmplibbackends_a_SOURCES) $(KPATHSEA_DEPEND) $(CAIRO_DEPEND) $(MPFR_DEPEND) + EXTRA_DIST += $(libmplib_web) -DISTCLEANFILES += $(nodist_libmplib_a_SOURCES) \ +DISTCLEANFILES += $(nodist_libmplibcore_a_SOURCES) $(nodist_libmplibbackends_a_SOURCES) \ mp-tangle mpmath-tangle mpmathbinary-tangle mpmathdecimal-tangle mpmathdouble-tangle \ mpstrings-tangle psout-tangle svgout-tangle pngout-tangle diff --git a/Build/source/texk/web2c/mplibdir/am/mplib.am b/Build/source/texk/web2c/mplibdir/am/mplib.am index 80f574837ae..f58658de18f 100644 --- a/Build/source/texk/web2c/mplibdir/am/mplib.am +++ b/Build/source/texk/web2c/mplibdir/am/mplib.am @@ -18,9 +18,13 @@ endif MP EXTRA_PROGRAMS += mpost mpost_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_INCLUDES) $(LIBPNG_INCLUDES) -I$(srcdir)/mplibdir -mpost_LDADD = libmplib.a $(KPATHSEA_LIBS) $(MPFR_LIBS) $(GMP_LIBS) \ +#mpost_LDADD = libmplib.a $(KPATHSEA_LIBS) $(MPFR_LIBS) $(GMP_LIBS) \ +# $(CAIRO_LIBS) $(PIXMAN_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) libmputil.a +mpost_LDADD = libmplibcore.a libmplibbackends.a $(KPATHSEA_LIBS) $(MPFR_LIBS) $(GMP_LIBS) \ $(CAIRO_LIBS) $(PIXMAN_LIBS) $(LIBPNG_LIBS) $(ZLIB_LIBS) libmputil.a + + call_mpost_CPPFLAGS = -DEXEPROG=\"mpost.exe\" nodist_call_mpost_SOURCES = callexe.c call_mpost_LDADD = @@ -55,7 +59,8 @@ mpxout-tangle: ctangle$(EXEEXT) mplibdir/mpxout.w tangle-sh ## MetaPost CWeb sources mpost_web = mplibdir/mpost.w mplibdir/mpxout.w -$(mpost_OBJECTS): $(nodist_mpost_SOURCES) libmplib.a $(LIBPNG_DEPEND) +#$(mpost_OBJECTS): $(nodist_mpost_SOURCES) libmplib.a $(LIBPNG_DEPEND) +$(mpost_OBJECTS): $(nodist_mpost_SOURCES) libmplibcore.a libmplibbackends.a $(LIBPNG_DEPEND) EXTRA_DIST += mplibdir/ChangeLog $(mpost_web) diff --git a/Build/source/texk/web2c/mplibdir/mp.w b/Build/source/texk/web2c/mplibdir/mp.w index 25b259ea094..adea5f18f17 100644 --- a/Build/source/texk/web2c/mplibdir/mp.w +++ b/Build/source/texk/web2c/mplibdir/mp.w @@ -1,4 +1,4 @@ -% $Id: mp.w 2117 2017-02-13 18:31:00Z luigi $ +% $Id: mp.w 2123 2017-03-08 12:49:56Z luigi $ % % This file is part of MetaPost; % the MetaPost program is in the public domain. @@ -176,18 +176,18 @@ static int DEBUGENVELOPECOUNTER=0; #include /* for struct tm \& co */ #include /* for |ZLIB_VERSION|, zlibVersion() */ #include /* for |PNG_LIBPNG_VER_STRING|, |png_libpng_ver| */ -#include /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */ -#include /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */ +/*\#include */ /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */ +/*\#include */ /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */ #include /* for |gmp_version| */ #include /* for |MPFR_VERSION_STRING|, |mpfr_get_version()| */ #include "mplib.h" #include "mplibps.h" /* external header */ -#include "mplibsvg.h" /* external header */ -#include "mplibpng.h" /* external header */ +/*\#include "mplibsvg.h" */ /* external header */ +/*\#include "mplibpng.h" */ /* external header */ #include "mpmp.h" /* internal header */ #include "mppsout.h" /* internal header */ -#include "mpsvgout.h" /* internal header */ -#include "mppngout.h" /* internal header */ +/*\#include "mpsvgout.h"*/ /* internal header */ +/*\#include "mppngout.h"*/ /* internal header */ #include "mpmath.h" /* internal header */ #include "mpmathdouble.h" /* internal header */ #include "mpmathdecimal.h" /* internal header */ @@ -202,7 +202,24 @@ mp_number ueps_ap; /* epsilon for above approximations */ boolean is_dxdy, is_dxindyin; /* END PATCH */ +@ We move the {\tt cairo} and {\tt pixman} libraries outside {\tt mp.w}, +to minimize dependencies. +@c +extern const char *COMPILED_CAIRO_VERSION_STRING; +extern const char* cairo_version_string (void); +extern const char *COMPILED_PIXMAN_VERSION_STRING; +extern const char* pixman_version_string (void); +extern void mp_png_backend_initialize (MP mp); +extern void mp_png_backend_free (MP mp); +extern int mp_png_gr_ship_out (void *hh, const char *options, int standalone); +extern int mp_png_ship_out (void *hh, const char *options); +extern void mp_svg_backend_initialize (MP mp); +extern void mp_svg_backend_free (MP mp); +extern int mp_svg_ship_out (mp_edge_object *hh, int prologues); +extern int mp_svg_gr_ship_out (mp_edge_object *hh, int prologues, int standalone); + +@ @c extern font_number mp_read_font_info (MP mp, char *fname); /* tfmin.w */ @h @; @; @@ -30652,8 +30669,8 @@ char *mp_metapost_version (void) { return mp_strdup (metapost_version); } void mp_show_library_versions (void) { - fprintf(stdout, "Compiled with cairo %s; using %s\n", CAIRO_VERSION_STRING, cairo_version_string()); - fprintf(stdout, "Compiled with pixman %s; using %s\n", PIXMAN_VERSION_STRING, pixman_version_string()); + fprintf(stdout, "Compiled with cairo %s; using %s\n", COMPILED_CAIRO_VERSION_STRING, cairo_version_string()); + fprintf(stdout, "Compiled with pixman %s; using %s\n",COMPILED_PIXMAN_VERSION_STRING, pixman_version_string()); fprintf(stdout, "Compiled with libpng %s; using %s\n", PNG_LIBPNG_VER_STRING, png_libpng_ver); fprintf(stdout, "Compiled with zlib %s; using %s\n", ZLIB_VERSION, zlibVersion()); fprintf(stdout, "Compiled with mpfr %s; using %s\n", MPFR_VERSION_STRING, mpfr_get_version()); diff --git a/Build/source/texk/web2c/mplibdir/mpmath.w b/Build/source/texk/web2c/mplibdir/mpmath.w index 6c0ee6d00a6..0195d608d85 100644 --- a/Build/source/texk/web2c/mplibdir/mpmath.w +++ b/Build/source/texk/web2c/mplibdir/mpmath.w @@ -1,4 +1,4 @@ -% $Id: mpmath.w 2118 2017-02-15 17:49:54Z luigi $ +% $Id: mpmath.w 2070 2015-10-06 10:35:23Z luigi $ % % This file is part of MetaPost; % the MetaPost program is in the public domain. diff --git a/Build/source/texk/web2c/mplibdir/mpmathdouble.w b/Build/source/texk/web2c/mplibdir/mpmathdouble.w index cb14969365f..92339309869 100644 --- a/Build/source/texk/web2c/mplibdir/mpmathdouble.w +++ b/Build/source/texk/web2c/mplibdir/mpmathdouble.w @@ -1,4 +1,4 @@ -% $Id: mpmathdouble.w 2118 2017-02-15 17:49:54Z luigi $ +% $Id: mpmathdouble.w 2091 2016-09-16 23:07:58Z luigi $ % % This file is part of MetaPost; % the MetaPost program is in the public domain. diff --git a/Build/source/texk/web2c/mplibdir/pngout.w b/Build/source/texk/web2c/mplibdir/pngout.w index b42bf7b3219..742522c2ff7 100644 --- a/Build/source/texk/web2c/mplibdir/pngout.w +++ b/Build/source/texk/web2c/mplibdir/pngout.w @@ -79,6 +79,9 @@ properly. That is |typedef struct pngout_data_struct * pngout_data|. #ifndef MPPNGOUT_H #define MPPNGOUT_H 1 #include "cairo.h" +const char *COMPILED_CAIRO_VERSION_STRING = CAIRO_VERSION_STRING; +#include "pixman.h" +const char *COMPILED_PIXMAN_VERSION_STRING = PIXMAN_VERSION_STRING; #define PNG_SKIP_SETJMP_CHECK 1 #include "png.h" #include "mplib.h" diff --git a/Build/source/texk/web2c/mplibdir/svgout.w b/Build/source/texk/web2c/mplibdir/svgout.w index b7316f3f98b..94801da5205 100644 --- a/Build/source/texk/web2c/mplibdir/svgout.w +++ b/Build/source/texk/web2c/mplibdir/svgout.w @@ -1340,3 +1340,4 @@ int mp_svg_ship_out (mp_edge_object *hh, int prologues) { else if (gr_type(p)==mp_stroked_code) { do_write_postscript(p,mp_stroked_object); } else if (gr_type(p)==mp_text_code) { do_write_postscript(p,mp_text_object); } } + -- cgit v1.2.3