diff options
author | Luigi Scarso <luigi.scarso@gmail.com> | 2017-03-11 00:31:06 +0000 |
---|---|---|
committer | Luigi Scarso <luigi.scarso@gmail.com> | 2017-03-11 00:31:06 +0000 |
commit | 7fa7e2984ec7346b13ce8d3a84826552d30f00ea (patch) | |
tree | f177da03ee3839977b28ee4f149cb612666eddde /Build/source/texk/web2c | |
parent | dedac2fcc2339e6d9cddef8edb046fd0e04c397e (diff) |
sync with luatex r.6287 and mpost r.2125
git-svn-id: svn://tug.org/texlive/trunk@43454 c570f23f-e606-0410-a88d-b1316a301751
Diffstat (limited to 'Build/source/texk/web2c')
46 files changed, 17123 insertions, 16119 deletions
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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @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 <bugs@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ 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 <http://www.gnu.org/licenses/>. */ + with LuaTeX; if not, see <http://www.gnu.org/licenses/>. -/* 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 <taco@luatex.org> 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<l;i++) { - lua_pushlstring(L,q,1); q++; - lua_rawseti(L,-2,n); n++; - } - free(orig); - return 1; - } - if (*(joiner+1) == '+') { - mult = 1; - while(*p==*joiner) { - p++; - l--; +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 */ } - q = p; - } - for (i=0;i<l;i++) { - if (*(p+i)==*joiner) { - *(p+i) = 0; - lua_pushlstring(L,q,((p+i)-q)); - lua_rawseti(L,-2,n); n++; - if (mult) { - while(*(p+i+1)==*joiner) { - i++; - } - } - q = p+i+1; + lua_replace(L, lua_upvalueindex(2)); + i = (unsigned char)*(s+ind); + lua_pushinteger(L, i); /* byte one */ + if (ind+1<(int)ls) { + i = (unsigned char)*(s+ind+1); + lua_pushinteger(L, i); /* byte two */ + } else { + lua_pushnil(L); /* odd string length */ } + return 2; } - if (mult && q==(p+l)) { - free(orig); - return 1; - } - if(q<=(p+l)) { - lua_pushlstring(L,q,strlen(q)); - lua_rawseti(L,-2,n); - } - free(orig); + return 0; /* string ended */ +} + +static int str_bytepairs (lua_State *L) { + luaL_checkstring(L, 1); + lua_settop(L, 1); + lua_pushinteger(L, 0); + lua_pushcclosure(L, bytepairs_aux, 2); return 1; -} +} -static int characters_aux (lua_State *L) { +static int bytes_aux (lua_State *L) { size_t ls; - char b[2]; + unsigned char i; 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)); - b[0] = *(s+ind); b[1] = 0; - lua_pushlstring(L, b, 1); + i = (unsigned char)*(s+ind); + lua_pushinteger(L, i); /* byte */ return 1; } return 0; /* string ended */ } - -static int str_characters (lua_State *L) { +static int str_bytes (lua_State *L) { luaL_checkstring(L, 1); lua_settop(L, 1); lua_pushinteger(L, 0); - lua_pushcclosure(L, characters_aux, 2); + lua_pushcclosure(L, bytes_aux, 2); return 1; } - static int utf_failed(lua_State *L, int new_ind) { static char fffd [3] = {0xEF,0xBF,0xBD}; lua_pushinteger(L, new_ind); /* iterator */ @@ -152,8 +123,7 @@ static int utfcharacters_aux (lua_State *L) { } } return utf_failed(L,ind+1); /* we found a follow byte! */ -} - +} static int str_utfcharacters (lua_State *L) { luaL_checkstring(L, 1); @@ -163,7 +133,6 @@ static int str_utfcharacters (lua_State *L) { return 1; } - static int utfvalues_aux (lua_State *L) { size_t ls; unsigned char i = 0; @@ -180,7 +149,7 @@ static int utfvalues_aux (lua_State *L) { if (i<0x80) { v = i; } else if (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<l;i++) { + lua_pushlstring(L,q,1); q++; + lua_rawseti(L,-2,n); n++; } - lua_replace(L, lua_upvalueindex(2)); - i = (unsigned char)*(s+ind); - lua_pushinteger(L, i); /* byte one */ - if (ind+1<(int)ls) { - i = (unsigned char)*(s+ind+1); - lua_pushinteger(L, i); /* byte two */ - } else { - lua_pushnil(L); /* odd string length */ + free(orig); + return 1; + } + if (*(joiner+1) == '+') { + mult = 1; + while(*p==*joiner) { + p++; + l--; } - return 2; + q = p; } - return 0; /* string ended */ -} - - -static int str_bytepairs (lua_State *L) { - luaL_checkstring(L, 1); - lua_settop(L, 1); - lua_pushinteger(L, 0); - lua_pushcclosure(L, bytepairs_aux, 2); + for (i=0;i<l;i++) { + if (*(p+i)==*joiner) { + *(p+i) = 0; + lua_pushlstring(L,q,((p+i)-q)); + lua_rawseti(L,-2,n); n++; + if (mult) { + while(*(p+i+1)==*joiner) { + i++; + } + } + q = p+i+1; + } + } + if (mult && q==(p+l)) { + free(orig); + return 1; + } + if(q<=(p+l)) { + lua_pushlstring(L,q,strlen(q)); + lua_rawseti(L,-2,n); + } + free(orig); return 1; } - - +#ifdef LuajitTeX + /* dump is built in */ +#else static int writer (lua_State *L, const void* b, size_t size, void* B) { (void)L; luaL_addlstring((luaL_Buffer*) B, (const char *)b, size); @@ -349,8 +345,19 @@ static int str_dump (lua_State *L) { luaL_pushresult(&b); return 1; } - - +#endif /*ifdef LuajitTeX*/ + +static int str_bytetable (lua_State *L) { + size_t l; + int i; + const char *s = luaL_checklstring(L, 1, &l); + lua_createtable(L,l,0); + for (i=0;i<l;i++) { + lua_pushinteger(L,(unsigned char)*(s+i)); + lua_rawseti(L,-2,i+1); + } + return 1; +} static const luaL_Reg strlibext[] = { {"utfvalues", str_utfvalues}, @@ -359,8 +366,13 @@ static const luaL_Reg strlibext[] = { {"characterpairs", str_characterpairs}, {"bytes", str_bytes}, {"bytepairs", str_bytepairs}, + {"bytetable", str_bytetable}, {"explode", str_split}, +#ifdef LuajitTeX + /* luajit has dump built in */ +#else {"dump", str_dump}, +#endif {NULL, NULL} }; @@ -374,4 +386,3 @@ void open_strlibext(lua_State * L) } lua_pop(L,1); } - diff --git a/Build/source/texk/web2c/luatexdir/lua/luainit.w b/Build/source/texk/web2c/luatexdir/lua/luainit.w index fe0c3de5eb0..de2bf47cc46 100644 --- a/Build/source/texk/web2c/luatexdir/lua/luainit.w +++ b/Build/source/texk/web2c/luatexdir/lua/luainit.w @@ -1,1095 +1,1153 @@ -% luainit.w
-%
-% Copyright 2006-2017 Taco Hoekwater <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @c
-
-#include "ptexlib.h"
-
-#include <kpathsea/c-stat.h>
-
-#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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @c + +#include "ptexlib.h" + +#include <kpathsea/c-stat.h> + +#include "lua/luatex-api.h" + +#include <locale.h> + +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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @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 *//* |[{<tex>}]| */
- lua_pushstring(L, tab); /* |[{<tex>},"dimen"]| */
- lua_newtable(L); /* |[{<tex>},"dimen",{}]| */
- lua_settable(L, -3); /* |[{<tex>}]| */
- /* fetch it back */
- lua_pushstring(L, tab); /* |[{<tex>},"dimen"]| */
- lua_gettable(L, -2); /* |[{<tex>},{<dimen>}]| */
- /* make the meta entries */
- luaL_newmetatable(L, mttab); /* |[{<tex>},{<dimen>},{<dimen_m>}]| */
- lua_pushstring(L, "__index"); /* |[{<tex>},{<dimen>},{<dimen_m>},"__index"]| */
- lua_pushstring(L, getfunc); /* |[{<tex>},{<dimen>},{<dimen_m>},"__index","getdimen"]| */
- lua_gettable(L, -5); /* |[{<tex>},{<dimen>},{<dimen_m>},"__index",<tex.getdimen>]| */
- lua_settable(L, -3); /* |[{<tex>},{<dimen>},{<dimen_m>}]| */
- lua_pushstring(L, "__newindex"); /* |[{<tex>},{<dimen>},{<dimen_m>},"__newindex"]| */
- lua_pushstring(L, setfunc); /* |[{<tex>},{<dimen>},{<dimen_m>},"__newindex","setdimen"]| */
- lua_gettable(L, -5); /* |[{<tex>},{<dimen>},{<dimen_m>},"__newindex",<tex.setdimen>]| */
- lua_settable(L, -3); /* |[{<tex>},{<dimen>},{<dimen_m>}]| */
- lua_setmetatable(L, -2); /* |[{<tex>},{<dimen>}]| : assign the metatable */
- lua_pop(L, 1); /* |[{<tex>}]| : 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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @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 *//* |[{<tex>}]| */ + lua_pushstring(L, tab); /* |[{<tex>},"dimen"]| */ + lua_newtable(L); /* |[{<tex>},"dimen",{}]| */ + lua_settable(L, -3); /* |[{<tex>}]| */ + /* fetch it back */ + lua_pushstring(L, tab); /* |[{<tex>},"dimen"]| */ + lua_gettable(L, -2); /* |[{<tex>},{<dimen>}]| */ + /* make the meta entries */ + luaL_newmetatable(L, mttab); /* |[{<tex>},{<dimen>},{<dimen_m>}]| */ + lua_pushstring(L, "__index"); /* |[{<tex>},{<dimen>},{<dimen_m>},"__index"]| */ + lua_pushstring(L, getfunc); /* |[{<tex>},{<dimen>},{<dimen_m>},"__index","getdimen"]| */ + lua_gettable(L, -5); /* |[{<tex>},{<dimen>},{<dimen_m>},"__index",<tex.getdimen>]| */ + lua_settable(L, -3); /* |[{<tex>},{<dimen>},{<dimen_m>}]| */ + lua_pushstring(L, "__newindex"); /* |[{<tex>},{<dimen>},{<dimen_m>},"__newindex"]| */ + lua_pushstring(L, setfunc); /* |[{<tex>},{<dimen>},{<dimen_m>},"__newindex","setdimen"]| */ + lua_gettable(L, -5); /* |[{<tex>},{<dimen>},{<dimen_m>},"__newindex",<tex.setdimen>]| */ + lua_settable(L, -3); /* |[{<tex>},{<dimen>},{<dimen_m>}]| */ + lua_setmetatable(L, -2); /* |[{<tex>},{<dimen>}]| : assign the metatable */ + lua_pop(L, 1); /* |[{<tex>}]| : 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 <bugs@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ 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 <stdio.h> + +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 <stdio.h> -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 <kpathsea/c-ctype.h> 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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-\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;
- @<Create a bunch of primitives@>;
- @<Create the math param primitives@>;
- @<Create another bunch of primitives@>;
-}
-
-
-@ These are in a separate module due to a CWEAVE limitation.
-
-@<Create a bunch of primitives@>=
-
- /*
- 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.
-
-@<Create the math param primitives@>=
- 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.
-
-@<Create another bunch of primitives@>=
- 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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +\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; + @<Create a bunch of primitives@>; + @<Create the math param primitives@>; + @<Create another bunch of primitives@>; +} + + +@ These are in a separate module due to a CWEAVE limitation. + +@<Create a bunch of primitives@>= + + /* + 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. + +@<Create the math param primitives@>= + 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. + +@<Create another bunch of primitives@>= + 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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @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 <inserted text> above.)",
- "With luck, this will get me unwedged. But if you",
- "really didn't forget anything, try typing `2' now; then",
- "my insertion and my current dilemma will both disappear.");
- error();
- }
-}
-
-@ 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 <box or rule><hskip or vskip>'.",
- "I found the <box or rule>, but there's no suitable",
- "<hskip or vskip>, so I'm ignoring these leaders.");
- back_error();
- flush_node_list(cur_box);
- }
- } 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 <box> 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<number> to \\cs'.",
- "I'm going to look for the \\cs now.");
- error();
- }
- get_r_token();
- p = cur_cs;
- read_toks(n, p, j);
- define(p, call_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<advance<multiply<divide|
-
-Compute the register location |l| and its type |p|; but |return| if invalid
-Here we use the fact that the consecutive codes |int_val..mu_val| and
-|assign_int..assign_mu_glue| correspond to each other nicely.
-
-@c
-void do_register_command(int a)
-{
- int p;
- halfword q = cur_cmd;
- halfword l = 0;
- if (q != register_cmd) {
- get_x_token();
- if ((cur_cmd >= 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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @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 <inserted text> above.)", + "With luck, this will get me unwedged. But if you", + "really didn't forget anything, try typing `2' now; then", + "my insertion and my current dilemma will both disappear."); + error(); + } +} + +@ 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 <box or rule><hskip or vskip>'.", + "I found the <box or rule>, but there's no suitable", + "<hskip or vskip>, so I'm ignoring these leaders."); + back_error(); + flush_node_list(cur_box); + } + } 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 <box> 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<number> to \\cs'.", + "I'm going to look for the \\cs now."); + error(); + } + get_r_token(); + p = cur_cs; + read_toks(n, p, j); + define(p, call_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<advance<multiply<divide| + +Compute the register location |l| and its type |p|; but |return| if invalid +Here we use the fact that the consecutive codes |int_val..mu_val| and +|assign_int..assign_mu_glue| correspond to each other nicely. + +@c +void do_register_command(int a) +{ + int p; + halfword q = cur_cmd; + halfword l = 0; + if (q != register_cmd) { + get_x_token(); + if ((cur_cmd >= 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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @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("<unknown noad type!>");
- 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 <delimiter code>'.",
- 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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @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("<unknown noad type!>"); + 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 <delimiter code>'.", + 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 <taco@@luatex.org>
-%
-% 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 <http://www.gnu.org/licenses/>.
-
-@ @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<c)) { \
- fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \
- (int)a, (int)b, (int)c, __LINE__,test_count); \
- confusion("node range test failed"); \
- } } while (0)
-
-#define dotest(a,b,c) do { \
- if (b!=c) { \
- fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \
- (int)a, (int)b, (int)c, __LINE__,test_count); \
- confusion("node test failed"); \
- } } while (0)
-
-#define check_action_ref(a) { dorangetest(p,a,var_mem_max); }
-#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); }
-#define check_token_ref(a) { \
- if (type(p) == whatsit_node) { \
- formatted_error("nodes","fuzzy token cleanup in whatsit node with id %i and subtype %i",type(p),subtype(p)); \
- } else { \
- formatted_error("nodes","fuzzy token cleanup in node with id %i",type(p)); \
- } \
-}
-
-#ifdef CHECK_NODE_USAGE
-
-static void check_static_node_mem(void)
-{
- dotest(zero_glue, width(zero_glue), 0);
- dotest(zero_glue, type(zero_glue), glue_spec_node);
- dotest(zero_glue, vlink(zero_glue), null);
- dotest(zero_glue, stretch(zero_glue), 0);
- dotest(zero_glue, stretch_order(zero_glue), normal);
- dotest(zero_glue, shrink(zero_glue), 0);
- dotest(zero_glue, shrink_order(zero_glue), normal);
-
- dotest(sfi_glue, width(sfi_glue), 0);
- dotest(sfi_glue, type(sfi_glue), glue_spec_node);
- dotest(sfi_glue, vlink(sfi_glue), null);
- dotest(sfi_glue, stretch(sfi_glue), 0);
- dotest(sfi_glue, stretch_order(sfi_glue), sfi);
- dotest(sfi_glue, shrink(sfi_glue), 0);
- dotest(sfi_glue, shrink_order(sfi_glue), normal);
-
- dotest(fil_glue, width(fil_glue), 0);
- dotest(fil_glue, type(fil_glue), glue_spec_node);
- dotest(fil_glue, vlink(fil_glue), null);
- dotest(fil_glue, stretch(fil_glue), unity);
- dotest(fil_glue, stretch_order(fil_glue), fil);
- dotest(fil_glue, shrink(fil_glue), 0);
- dotest(fil_glue, shrink_order(fil_glue), normal);
-
- dotest(fill_glue, width(fill_glue), 0);
- dotest(fill_glue, type(fill_glue), glue_spec_node);
- dotest(fill_glue, vlink(fill_glue), null);
- dotest(fill_glue, stretch(fill_glue), unity);
- dotest(fill_glue, stretch_order(fill_glue), fill);
- dotest(fill_glue, shrink(fill_glue), 0);
- dotest(fill_glue, shrink_order(fill_glue), normal);
-
- dotest(ss_glue, width(ss_glue), 0);
- dotest(ss_glue, type(ss_glue), glue_spec_node);
- dotest(ss_glue, vlink(ss_glue), null);
- dotest(ss_glue, stretch(ss_glue), unity);
- dotest(ss_glue, stretch_order(ss_glue), fil);
- dotest(ss_glue, shrink(ss_glue), unity);
- dotest(ss_glue, shrink_order(ss_glue), fil);
-
- dotest(fil_neg_glue, width(fil_neg_glue), 0);
- dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node);
- dotest(fil_neg_glue, vlink(fil_neg_glue), null);
- dotest(fil_neg_glue, stretch(fil_neg_glue), -unity);
- dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil);
- dotest(fil_neg_glue, shrink(fil_neg_glue), 0);
- dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal);
-}
-
-static void node_mem_dump(halfword p)
-{
- halfword r;
- for (r = my_prealloc + 1; r < var_mem_max; r++) {
- if (vlink(r) == p) {
- halfword s = r;
- while (s > 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("<glue_spec ");
- print_spec(p, NULL);
- 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 <taco@@luatex.org> +% +% 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 <http://www.gnu.org/licenses/>. + +@ @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<c)) { \ + fprintf(stdout,"For node p:=%d, 0<=%d<%d (l.%d,r.%d)\n", \ + (int)a, (int)b, (int)c, __LINE__,test_count); \ + confusion("node range test failed"); \ + } } while (0) + +#define dotest(a,b,c) do { \ + if (b!=c) { \ + fprintf(stdout,"For node p:=%d, %d==%d (l.%d,r.%d)\n", \ + (int)a, (int)b, (int)c, __LINE__,test_count); \ + confusion("node test failed"); \ + } } while (0) + +#define check_action_ref(a) { dorangetest(p,a,var_mem_max); } +#define check_attribute_ref(a) { dorangetest(p,a,var_mem_max); } +#define check_token_ref(a) { \ + if (type(p) == whatsit_node) { \ + formatted_error("nodes","fuzzy token cleanup in whatsit node with id %i and subtype %i",type(p),subtype(p)); \ + } else { \ + formatted_error("nodes","fuzzy token cleanup in node with id %i",type(p)); \ + } \ +} + +#ifdef CHECK_NODE_USAGE + +static void check_static_node_mem(void) +{ + dotest(zero_glue, width(zero_glue), 0); + dotest(zero_glue, type(zero_glue), glue_spec_node); + dotest(zero_glue, vlink(zero_glue), null); + dotest(zero_glue, stretch(zero_glue), 0); + dotest(zero_glue, stretch_order(zero_glue), normal); + dotest(zero_glue, shrink(zero_glue), 0); + dotest(zero_glue, shrink_order(zero_glue), normal); + + dotest(sfi_glue, width(sfi_glue), 0); + dotest(sfi_glue, type(sfi_glue), glue_spec_node); + dotest(sfi_glue, vlink(sfi_glue), null); + dotest(sfi_glue, stretch(sfi_glue), 0); + dotest(sfi_glue, stretch_order(sfi_glue), sfi); + dotest(sfi_glue, shrink(sfi_glue), 0); + dotest(sfi_glue, shrink_order(sfi_glue), normal); + + dotest(fil_glue, width(fil_glue), 0); + dotest(fil_glue, type(fil_glue), glue_spec_node); + dotest(fil_glue, vlink(fil_glue), null); + dotest(fil_glue, stretch(fil_glue), unity); + dotest(fil_glue, stretch_order(fil_glue), fil); + dotest(fil_glue, shrink(fil_glue), 0); + dotest(fil_glue, shrink_order(fil_glue), normal); + + dotest(fill_glue, width(fill_glue), 0); + dotest(fill_glue, type(fill_glue), glue_spec_node); + dotest(fill_glue, vlink(fill_glue), null); + dotest(fill_glue, stretch(fill_glue), unity); + dotest(fill_glue, stretch_order(fill_glue), fill); + dotest(fill_glue, shrink(fill_glue), 0); + dotest(fill_glue, shrink_order(fill_glue), normal); + + dotest(ss_glue, width(ss_glue), 0); + dotest(ss_glue, type(ss_glue), glue_spec_node); + dotest(ss_glue, vlink(ss_glue), null); + dotest(ss_glue, stretch(ss_glue), unity); + dotest(ss_glue, stretch_order(ss_glue), fil); + dotest(ss_glue, shrink(ss_glue), unity); + dotest(ss_glue, shrink_order(ss_glue), fil); + + dotest(fil_neg_glue, width(fil_neg_glue), 0); + dotest(fil_neg_glue, type(fil_neg_glue), glue_spec_node); + dotest(fil_neg_glue, vlink(fil_neg_glue), null); + dotest(fil_neg_glue, stretch(fil_neg_glue), -unity); + dotest(fil_neg_glue, stretch_order(fil_neg_glue), fil); + dotest(fil_neg_glue, shrink(fil_neg_glue), 0); + dotest(fil_neg_glue, shrink_order(fil_neg_glue), normal); +} + +static void node_mem_dump(halfword p) +{ + halfword r; + for (r = my_prealloc + 1; r < var_mem_max; r++) { + if (vlink(r) == p) { + halfword s = r; + while (s > 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("<glue_spec "); + print_spec(p, NULL); + 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 <cworth@cworth.org> + * Adrian Johnson <ajohnson@redneon.com> + */ + +#include <stdio.h> +#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 <windows.h> +#include <io.h> + +/* 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 <time.h> /* for struct tm \& co */ #include <zlib.h> /* for |ZLIB_VERSION|, zlibVersion() */ #include <png.h> /* for |PNG_LIBPNG_VER_STRING|, |png_libpng_ver| */ -#include <pixman.h> /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */ -#include <cairo.h> /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */ +/*\#include <pixman.h>*/ /* for |PIXMAN_VERSION_STRING|, |pixman_version_string()| */ +/*\#include <cairo.h>*/ /* for |CAIRO_VERSION_STRING|, |cairo_version_string()| */ #include <gmp.h> /* for |gmp_version| */ #include <mpfr.h> /* 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 @<Declarations@>; @<Basic printing procedures@>; @@ -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); } } + |